; I T S DISK SERVICE ROUTINES AND FILE SYSTEM  -*-MIDAS-*-
;;; Copyright (c) 1999 Massachusetts Institute of Technology
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License as
;;; published by the Free Software Foundation; either version 3 of the
;;; License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, see https://gnu.org/licenses or
;;; write to:
;;;  Free Software Foundatiom, Inc.
;;;  51 Franklin St, Fifth Floor
;;;  Boston, MA 02110-1301
;;;  USA


DSKVRS==.IFNM2
IFN DC10P+RP10P+RH10P+RH11P-1, .ERR WRONG NUMBER OF DISK CONTROLLERS

;GET DISK PHYSICAL PARAMETERS BY .INSRT'ING APPROPRIATE FILE

IFN DC10P,[
$INSRT DC10
]

IFN RP10P,[
$INSRT RP10
]

IFN RH10P,[
$INSRT RH10
]

IFN RH11P,[
$INSRT RH11
IFE KS10P, .ERR RH11 on something other than a KS10?
IFN RP06P+RM03P-1, .ERR WRONG NUMBER OF KINDS OF DISK DRIVE
IFN RP06P,[
$INSRT RP06
]
IFN RM03P,[
$INSRT RM03
]
]

;GET FILE SYSTEM DEFINITIONS:
;	MFD	MASTER FILE DIRECTORY
;	TUT	TRACK (BLOCK) UTILIZATION TABLE
;	UFD	USER FILE DIRECTORY

$INSRT FSDEFS

IFN T300P,[
$INSRT T300
]

SUBTTL MISC FILE SYSTEM CALLS

ADMPCH:	UMOVE A,(J)	;SET OR READ STATUS OF DUMP BIT OF FILE OPEN ON
			; CHNL IN AC(RH)
	MOVEI T,POPJ1	;MAKE CHNDCD SKIP RETURN IF ALL OK.
	PUSHJ P,CHNDCD	;DECODE RH(A) AS CHNL NUM.
	 JRST ILUUO	;NO SKIP IMPLIES BAD CHNL NUM.
	TLNN R,%CLSQ
	 JRST ILUUO	;NOT DISK CHANNEL.
	PUSHJ P,QCHNLT	;DECODE THE CHNL FURTHER.
	MOVSI B,400000
	TLNE A,400000
	 IORM B,UNRNDM(C)
	TLNE A,200000
	 ANDCAM B,UNRNDM(C)
	MOVE D,QACTB
	TLNE A,600000
	 IORM D,QSNLCN(H)
	LDB A,[430100,,UNRNDM(C)]
	PUSHJ P,QUDULK
	JRST APTUAJ

NRFDATE:	PUSHJ P,QCHNLT	;READ FILE CREATION DATE WORD
	MOVE A,UNDATE(C)
NRDM1:	PUSHJ P,QUDULK
	JRST POPJ1	;STORE BACK IN USER MEM

NSRDATE:	PUSHJ P,QCHNLT	; SET REFERENCE DATE
	HLRS B
	DPB B,[UNREFD+UNREF(C)]
	JRST NSDM1

NRESRDT:	PUSHJ P,QCHNLT	; RESTORE REFERENCE DATE
	HLRZ A,OLDRDT(R)	; WHOEVER CHOSE R IN QCHNLC SHOULD BE SHOT
	DPB A,[UNREFD+UNREF(C)]
	JRST NSDM1

NSFDATE:	PUSHJ P,QCHNLT	;SET FILE CREATION DATE WORD
	MOVEM B,UNDATE(C)
NSDM1:	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
QUDUL1:	AOS (P)
	JRST QUDULK


NRDMPBT:	PUSHJ P,QCHNLT	;READ FILE DUMPED BIT
	LDB A,[430100,,UNRNDM(C)]
	JRST NRDM1

NSDMPBT:	PUSHJ P,QCHNLT	;SET FILE DUMPED BIT
	DPB B,[430100,,UNRNDM(C)]
	JRST NSDM1

NLNKEDP:
	PUSHJ P,QCHNLT		;RETURN NONZERO IF WE TRACED LINKS TO GET THIS FILE.
	MOVE T,QSRAC(R)
	LDB A,[.BP (%QALOP),T]
	JRST NRDM1

NFILBLK:PUSHJ P,QCHNLT	;READ ALL 5 PARAMETERS OF NAME AREA
	MOVE A,(C)
	MOVE B,1(C)
	MOVE D,3(C)
	MOVE E,4(C)
	MOVE C,2(C)
	JRST NRDM1	; UNLOCK DIR AND STORE ARGUMENTS

QCHNLT:	TLNN R,%CLSQ	;IS CHNL A DISK CHNL?
	 JRST [SUB P,[1,,1] ? JRST OPNL34]
	HLRZ R,H
	MOVE H,QUDPR(R)
	PUSHJ P,QUDLK
	MOVE C,QSNLCN(H)
	ADD C,QUDFPR(R)
	POPJ P,

IFN QAUTHP,[
ASAUTH:	PUSHJ P,QCHNLT	;SET AUTHOR OF FILE
	PUSH P,C
	JUMPE B,ASAUT4	;SETTING AUTHOR TO 0
	MOVE J,QSNMI(H)
	CAMN B,QSNUD(H)
	 JRST ASAUT1
	MOVE C,B
	PUSHJ P,QFL
	 SKIPA
	  JRST ASAUT1
	SETZM C
	MOVEI J,5
	MOVE TT,[440600,,B]
	MOVE I,[440600,,C]
	ILDB D,TT
	IDPB D,I
ASAUT2:	ILDB D,TT
	CAIG D,'Z
	CAIGE D,'A
	 JRST ASAUT3
	IDPB D,I
	SOJG J,ASAUT2
	JRST ASAUT4

ASAUT3:	PUSHJ P,QFL
ASAUT4:	 SETOM J
ASAUT1:	POP P,C
	DPB J,[UNAUTH+UNREF(C)]
	JRST NSDM1

ARAUTH:	PUSHJ P,QCHNLT	;READ CREATOR OF FILE
	LDB B,[UNAUTH+UNREF(C)]
	MOVEI A,0	;RETURN VALUE IS 0 IF UNKNOWN OR ILLEGAL AUTHOR
	CAIL B,NUDSL
	 JRST NRDM1
	LSH B,1
	MOVEI B,2000-<NUDSL*2>(B)
	ADD B,QMDRO
	MOVE A,(B)
	JRST NRDM1
];QAUTHP

ASREAP:	PUSHJ P,QCHNLT		;SET NO REAP BIT
	DPB B,[.BP (UNREAP),UNRNDM(C)]
	JRST NSDM1

ARQDAT:	CONO PI,CLKOFF	;GET DSK TIME AND DATE
	SKIPL A,QDATE
	HRR A,TIMOFF
	MOVE B,QDATEI	;2ND VALUE = DATE & TIME SYS CAME UP, IN DISK FORMAT.
	CONO PI,CLKON
	JRST POPJ1

ADSKUP:	PUSHJ P,QCHNLT	;SET CREATION DATE, REF DATE AND CLEAR DUMP BIT
	CONO PI,CLKOFF
	SKIPL TT,QDATE
	 HRR TT,TIMOFF
	CONO PI,CLKON
	MOVEM TT,UNDATE(C)
	HLRS TT
	DPB TT,[UNREFD+UNREF(C)]
	MOVSI TT,UNDUMP
	ANDCAM TT,UNRNDM(C)
	JRST NSDM1

DELEWO:	TLNN R,%CLSQ	;DELETE WHILE OPEN
	 JRST OPNL34	;WRONG TYPE DEVICE
	PUSHJ P,QCHNLT
	PUSH P,W
	PUSH P,C
	MOVEI W,4
	MOVE A,UNFN1(C)	;Get names of file being hacked.
	MOVE B,UNFN2(C)
	MOVE C,QSNUD(H)
	PUSHJ P,MNGDIR	;If this is a sanctified dir
	 PUSHJ P,SYSDSK	; get SYS job to tattle about it.
	POP P,C
	POP P,W
	MOVSI T,%QADEL	;SET DELETE BIT IN QSK CHNL
	IORM T,QSRAC(R)
	MOVSI T,UNCDEL	;AND IN FILE
	IORM T,UNRNDM(C)
	JRST NSDM1

;.CALL FILLEN
;RETURNS (1) FILE LENGTH IN BYTES (BYTE SIZE CURRENTLY OPEN IN)
;	 (2) BITS PER BYTE (BYTE SIZE CURRENTLY OPEN IN)
;	 (3) FILE LENGTH IN BYTES (BYTE SIZE WRITTEN IN)
;	 (4) BITS PER BYTE (BYTE SIZE WRITTEN IN)

NFILLEN:PUSHJ P,QCHNLT
	MOVE A,QSRAC(R)
	TLNE A,%QALNK
	 JRST OPNL34	;NOT A FILE
	SKIPGE QSCRW(R)
	 TLNE A,%QAMWO
	  JRST NFILL1	;READ CHANNEL OR WRITE-OVER, GET FILE'S STORED LENGTH.
	MOVE A,QFBLNO(R);NORMAL WRITE, GET ACCESS POINTER.
	SKIPGE QSMDN(R)
	 JRST NFILL2	;NO BUFFER ACTIVE
	ADD A,QMPBSZ(R)	;END OF CURRENT BUFFER
	SUB A,QSMPRC(R)	;BACK UP TO CURRENT LOC
NFILL2:	PUSHJ P,QUDULK
	LDB B,[QSBSIZ(R)]	;CURRENT BYTE SIZE
	MOVE D,B	;WRITTEN BYTE SIZE SAME AS CURRENT
	MOVE C,A	;WRITTEN LENGTH SAME AS CURRENT
	JRST POPJ1

NFILL1:	LDB TT,[UNDSCP+UNRNDM(C)]
	IDIVI TT,UFDBPW
	HLL TT,QBTBLI(I)	;GET DESCRIPTOR POINTER
	LDB E,[UNWRDC+UNRNDM(C)]
	SKIPN E
	 MOVEI E,2000		;E GETS NUMBER OF WORDS IN LAST BLOCK
	LDB D,[UNBYTE+UNREF(C)]	;D GETS BYTE INFO
	ANDI C,-2000		;C GETS BASE ADDR OF DIR
	ADDI TT,UDDESC(C)	;TT GETS DESC PNTR
	SETOM A			;INITIALIZE NUMBER OF BLOCKS IN FILE
	PUSHJ P,NFLLN1		;A GETS NUMBER OF BLOCKS IN FILE MINUS ONE
	PUSHJ P,QUDULK
	IMULI A,2000		;NUMBER OF WORDS IN COMPLETE BLOCKS
	ADD A,E			;ADD WORDS IN LAST BLOCK
	PUSHJ P,QBDCD		;D GETS BYTE SIZE, E NUMBER OF RESIDUE BYTES
	MOVEI Q,36.
	IDIV Q,D		;Q GETS BYTES PER WORD
	MOVE C,A		;C WRITTEN LENGTH, A CURRENT (B.S. DIFFERENT)
	IMUL C,Q		;CONVERT WORD LENGTH TO BYTES
	SUB C,E			;C NOW HAS CORRECT WRITTEN LENGTH
	IMUL E,D		;E GETS NUMBER OF RESIDUE BITS
	LDB B,[QSBSIZ(R)]	;B GETS BYTE SIZE OPENED IN
	IMULI A,@QSBYTE(R)	;A GETS LENGTH IN THOSE SIZE BYTES
	IDIV E,B		;NUMBER OF RESIDUE BYTES, -ROUNDING DOWN-
	SUB A,E			;ADJUST THE LENGTH
	JRST POPJ1		;RETURN VALUES IN A,B,C,D

NFLLN2:	ADD A,B		; NEXT N BLOCKS
NFLLN1:	ILDB B,TT	; GET NEXT DESC BYTE
	JUMPE B,CPOPJ	; NO MORE
	CAIG B,UDTKMX
	 JRST NFLLN2	; TAKE-N
	CAIGE B,UDWPH
	 AOJA A,NFLLN1	; SKIP-N, TAKE-1
	CAIN B,UDWPH
	 JRST NFLLN1	; IGNORE WRITE-PLACEHOLDER
REPEAT NXLBYT, IBP TT	; LOAD-ADDRESS, TAKE-1
	AOJA A,NFLLN1

;DIRSIZ - READ OR SET DISK QUOTAS, READ # BLOCKS IN ALL FILES IN DIRECTORY.
;1ST VALUE IS GRIM REAP QUOTA,,DIR SIZE
;2ND VALUE IS 0 OR PACK#,,ALLOCATION FOR DIR ALLOCATED TO SPECIFIC PACK
;LH OF 2ND ARG SETS GRIM REAP QUOTA; 3RD ARG SETS PACK#,,ALLOCATION
NDIRSI:	MOVE D,C
	PUSHJ P,QCHNLT
	MOVE C,QSNLCN(H)
	MOVE A,UDBLKS(C)
	JUMPL B,NDIRS1
	CAIE W,1
	 HLLM B,UDBLKS(C)
NDIRS1:	MOVE B,UDALLO(C)
	CAILE W,2
	 MOVEM D,UDALLO(C)
IFN QRSRVP,[
	HLRZS D			;PACK ALLOCATED TO
	SETO E,
	JUMPE D,NDIRS2
	MOVEI E,NQS-1		;UPDATE ALLOC DRIVE NUM
	CAME D,QPKID(E)
	 SOJGE E,.-1
NDIRS2:	MOVEM E,QSALLO(H)
];QRSRVP
	JRST NSDM1

SUBTTL DISK OPEN ROUTINES

%DO==1,,525252	;Special mode bits for disk opens.
%DOWOV==100000	;Write over mode
%DONRF==10	;Don't set ref-date
%DONLK==20	;Don't chase links; actually open the link itself.
%DORWT==40	;Make readers wait (used on opens for write or write-over).

IFN TPLP+UNSPLP,[
TPLO:	MOVE C,[SIXBIT /.LPTR./] ;PSEUDO LINE PRINTER (DISC)
	JUMPGE D,TPLO2
	MOVE B,UNAME(U)
	AOS A,TPLFNO	;GENERATE RANDOM FILE NAME
	JRST TPLO2
]

COMO:	SKIPA C,[SIXBIT /COMMON/]	;.OPEN ENTRY FOR "COM" DEVICE
SYSO:	 MOVSI C,(SIXBIT /SYS/)		; " FOR "SYS" DEVICE
TPLO2:	MOVEM C,USYSN1(U)
	MOVNI I,1
	JRST QSKO

QSKPO:	MOVSI TT,-NQS	;PACK # OPEN
	CAME I,QPKID(TT)
	 AOBJN TT,.-1	;I <= PHYSICAL UNIT WITH SPECIFIED PACK
	JUMPL TT,QSKPO1
	PUSHJ P,QPKNFP
	 JRST QSKPO
	CAIE W,4	;IF RENAME/DELETE, OK, DOESN'T TOUCH FILE ANYWAY
	 JRST OPNL16	;OTHERWISE COMPLAIN PACK NOT MOUNTED
QSKPO1:	HRRZ I,TT
QSKUO:	CAIGE I,NQS	;DISK UNIT # OPEN
	 SKIPE QACT(I)	;I <= UNIT #
	  JRST OPNL10	;BAD UNIT # OR NOT ENGAGED
IFE MCOND DM,[	;; Security check in reserved pack feature only on DM.
IFN QRSRVP,[
	JUMPN W,QSKOB	;WRITE-OVER OR RENAME OK.
	JUMPGE D,QSKOB	;READ OK.
	SKIPE QRESRV(I)	;WRITE: IS PACK ON THIS DRIVE RESERVED?
	 JRST OPNL10	;YES, CAN'T WRITE ON IT.
];QRSRVP
];DM
	JRST QSKOB

;DNRF: DEVICE IS LIKE DSK: BUT DOESN'T SET REFERENCE DATE
DNRFO:	TRO D,%DONRF/2	;SET MODE BIT AND DROP INTO QSKO

; Regular DSK: device
;
; RH(D) has open mode, rotated 1 bit right.
; W has operation code (0=r/w, 2=link, 4=del/rnm, 1=wov)

QSKO:	MOVNI I,1	;DSK OPEN, I <= # DETERMINED BY SYS
QSKOB:	MOVEM I,EPDL(U)	;SAVE DISK #
	MOVE C,USYSN1(U)
	CAIN W,4
	 JUMPE A,QSKOB2	;IF RENAME OF OPEN FILE TRAP OUT
QSKOA:	MOVE I,MDSK	;IS MFD IN?
	PUSHJ P,QMCH1	;READ IN MFD OF MASTER DISK
	CLEARM QLD(U)	;LINK DEPTH
QSKOL:	PUSHJ P,MFDCK
	 JRST SYSDS2
	JUMPN W,OPNL11
	JUMPL D,OPNL11	;MUST BE NORMAL READ
	JRST QMLSTF	;USER WANT TO READ THE MASTER DIRECTORY

MFDCK:	CAMN A,[SIXBIT /M.F.D./]
	CAME B,[SIXBIT /(FILE)/]
	 POPJ P,
	JRST POPJ1

SYSDS2:	PUSHJ P,QFLD	;H <= PTR TO USER DIR TABLE
	 PUSHJ P,QSKO1	;LOSER DIR NOT IN CORE
	SKIPG QSNNR(H)
	 BUG
	PUSHJ P,QUDLK	;RETURN WITH SOS OF QSNNR ON LSWPR
	MOVSI TT,40000
	TDNE TT,QSNLCN(H)
	 JRST QSKDP1	;PAW OVER USER DIRECTORY
QSKDP2:	PUSHJ P,QUDULK
	PUSHJ P,FLDRCK
	 JRST QSKDP9	;NOT SPECIAL DIRECTORY FILE
	JUMPN W,QPNL11
	JUMPL D,QPNL11
	JRST QLISTF	;USER WANTS TO READ HIS USER DIRECTORY

QSKDP9:	JUMPN W,QSKDPY	;If doing IO?
	 SKIPL D	; and reading
	  JRST QSKDPZ	;  then don't bother with "security".
QSKDPY:	PUSHJ P,MNGDIR	;Else if this is a sanctified dir
	 PUSHJ P,SYSDSK	; get SYS job to tattle about it.
QSKDPZ:	JUMPL D,QWRO	;WRITE
	CAIN W,4
	 JRST QRNAM	;RENAME/DEL
	CAIN W,2
	 JRST QALINK	;MAKE LINK
	JUMPN W,QPNL22
QWROR:	JUMPE A,QPNL11	;ALSO ENTER FOR VARIOUS WRITE OVER, COPY OVER MODES
	JUMPE B,QPNL11
	PUSHJ P,QCHNF	;GET CHANNEL FOR READ
	PUSHJ P,LOSSET
	    QCHNRT
	PUSHJ P,QUDLK	;LOCK USER DIR
	PUSHJ P,QLOOK	;LOOK UP FILE
	 JRST [	PUSHJ P,QROR1C	;Not found => decide whethe error or wait.
		 POPJ P,	;Error (error code already set up).
		PUSHJ P,QUDULK	;If retrying, first unlock directory,
		PUSHJ P,LSWPOP	;Return the channel,
		PUSHJ P,UDELAY	;Wait a little while,
		JRST QWROR]	;Try again.
	TRNE D,3	;SKIP IF UNIT ASCII MODE
	 JRST QSKO2
	MOVE J,[440700,,5] ;BYTE SIZE IS 7 BITS
	MOVEM J,QSBYTE(E)
QSKO2:	HLLZ J,QSBYTE(E) ;GET PROPER BYTE POINTER LH
	MOVEM J,QSMPRP(E)
	TRNN D,%DONRF/2	;3.4 BIT IN OPEN IMPLIES DONT SET REFERENCE DATE
	 PUSHJ P,QFREF	;"REFERENCE" FILE
	MOVE C,Q
	SUB C,QSNLCN(H)
	HRRZM C,QUDFPR(E)	;SET UP PTR FROM CHNL TO FILE NAME AREA
	LDB TT,[UNDSCP+UNRNDM(Q)]	;FOUND FILE SET UP CHNL
	MOVEM TT,QDIRP(E)	;SET UP CHAR PTR TO DESC AREA
	MOVE C,UNRNDM(Q)
	TLNE C,UNLINK
	 JRST QLINK	;FILE IS A LINK
	MOVSI C,%QALOP
	SKIPE QLD(U)	;IF WE TRACED A LINK TO OPEN THE FILE, REMEMBER THAT.
	 IORM C,QSRAC(E)
	MOVSI C,%QARWT
	TRNE D,%DORWT/2
	 IORM C,QSRAC(E)
	LDB J,[UNPKN+UNRNDM(Q)]	;GET PACK NUMBER FILE IS ON
	MOVSI I,-NQS
	CAME J,QPKID(I)
	 AOBJN I,.-1	;TRANSLATE LOGICAL TO PHYSICAL DISK UNIT
	JUMPGE I,QPKNF	;PACK NOT ON *
	HRRZM I,QDSKN(E)	;SET CHNL DISK NUMBER
	JUMPL D,QWROR1	;REALLY WANT TO WRITE OVER, ETC
	MOVEI TT,%QMRD	;PUT CHANNEL IN NORMAL READ MODE
	HRRM TT,QSRAC(E)
QOEX1:	PUSHJ P,QUDULK	;UNLOCK USER DIR
	PUSHJ P,LSWDEL	;QUSR ENTRY
	PUSHJ P,LSWDEL	;QSNNR ENTRY
	SKIPG QSNNR(H)
	 BUG
	PUSHJ P,QSTRTR
	MOVE C,D
	ROT C,1
	HRL A,E
	JSP Q,OPSLC7	;SET UP IOCHNM AND DEPART
	    DQUAI,,DQUAO
	    DQBI,,DQBO
	    DQUII,,DQUIO
	    DQBI,,DQBO

QWROR1:	SOJN W,OPNL12	;NORMAL WRITE OVER MODE
	HRRZS H		;CLEAR GARBAGE IN USER DIR NUM
	HRRZS E		;CLEAR GARBAGE IN CHNL NUM
	HRRZ J,QUDFPR(E) ;PICK UP LOCN OF FILE WITH U.F.D
	MOVSI I,-NQCHN	;MAKE SURE THIS FILE NOT OPEN FOR READING
QROR1A:	CAIE E,(I)	;DONT GET FAKED OUT BY OWN CHNL
	 SKIPGE QUSR(I)
	  JRST QROR1B
	CAMN H,QUDPR(I)
	 CAME J,QUDFPR(I)
	  JRST QROR1B
	JRST  OPNL23

QROR1B:	AOBJN I,QROR1A
	PUSHJ P,QAUTH
	MOVSI TT,UNWRIT
	IORM TT,UNRNDM(Q)	;SET WRITE BIT
	MOVSI TT,UNDUMP
	ANDCAM TT,UNRNDM(Q)
	PUSH P,D
	PUSH P,Q
	PUSH P,R
	PUSH P,E	;CONVERT FROM ORIGINAL BYTE SIZE TO ONE OPENED IN NOW
	LDB D,[UNBYTE+UNREF(Q)]
	PUSHJ P,QBDCD	;GET INFO FROM ORIGINAL WRITE OF FILE
	MOVEI Q,36.
	IDIV Q,D	;BYTES PER WORD
	SUB Q,E		;# VALID BYTES
	IMUL Q,D	;VALID BITS IN LAST WORD
	POP P,E		;RESTORE QSK CHNL #
	HRRZ R,QSBYTE(E);BYTES PER WORD IN NEW BYTE SIZE
	LDB J,[QSBSIZ(E)] ;BITS PER BYTE IN NEW BYTE SIZE
	IDIV Q,J	;NUMBER OF NEW-SIZE BYTES IN LAST WORD
	SUB R,Q		;RESIDUE IN NEW-SIZE BYTES
	LDB Q,[QSBSIZ(E)] ;NEW BYTE SIZE
	PUSHJ P,QBENC	;RH(Q) GETS NEW BYTE INFO
	MOVE R,-1(P)
	DPB Q,[UNBYTE+UNREF(R)] ;CLOBBER FILE'S BYTE SIZE
	POP P,R
	POP P,Q
	POP P,D
	CONO PI,CLKOFF
	SKIPL TT,QDATE
	 HRR TT,TIMOFF
	CONO PI,CLKON
	MOVEM TT,UNDATE(Q)
	HLRS TT
	DPB TT,[UNREFD+UNREF(Q)]
	HLLM TT,OLDRDT(E)
	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
	MOVSI TT,%QAMWO		;WRITE OVER MODE
	IORM TT,QSRAC(E)	;LEAVE %QMIDL UNTIL FIRST .IOT SINCE
	JRST QOEX1		; THE USER WILL PROBABLY DO A .ACCESS

;Call here if lookup fails on open.
;Either signal some error and return,
;or skip-return if caller should wait and retry the lookup.
;Assumes H has dir slot, Q has address of filename block,
;E has channel number allocated for this open.
;Clobbers C, J.
QROR1C:	HRRZ C,QSNLCN(H)
	SUBI Q,-LUNBLK(C)
	SKIPL Q		;SEE IF STILL POINTED INSIDE DIR
	CAILE Q,2000-LUNBLK
	 JRST QROR1D	;NO - REALLY FNF
	ADDI Q,(C)
	CAMN A,UNFN1(Q)	;DO NAMES MATCH?
	CAME B,UNFN2(Q)
	 JRST QROR1D	;NO - REALLY FNF
	HRRZ J,Q	;Find channel that has this file open.
	SUB J,QSNLCN(H)
	ANDI J,-1
	HRRZS H		;Clear garbage in user dir num
	HRRZS E		;Clear garbage in chnl num
	MOVSI I,-NQCHN
QROR1E:	CAIE E,(I)	;Dont get faked out by our own chnl.
	 SKIPGE QUSR(I)
	  JRST QROR1F
	CAMN H,QUDPR(I)
	 CAME J,QUDFPR(I)
	  JRST QROR1F
;Found the channel.  Does it want us to wait?
	MOVE I,QSRAC(I)
	TLNN I,%QARWT
	 JRST OPNL23	;He didn't say so => get "file locked".
	JRST POPJ1

QROR1F:	AOBJN I,QROR1E
	JRST POPJ1	;Cannot find channel => maybe was closed.  Retry.

QROR1D:	SKIPN QLD(U)
	 JRST OPNL4	;FILE NOT FOUND
	JRST OPNL47	;LINK WAS FOUND, BUT NOT THE FILE IT POINTED TO

;COME HERE FOR RENAME-WHILE-OPEN
QSKOB2:	HRRZ A,B	;DO RENAME WHILE OPEN HERE TO AVOID
	CAIL A,20	;IN CASE SYS NAME HAS CHANGED
	 JRST OPNL14
	SKIPE SRN3(U)
	 SKIPN SRN4(U)
	  JRST OPNL11
	ADD A,U
	HLRZ E,IOCHNM(A)
	MOVE H,QUDPR(E)
	MOVE A,SRN3(U)
	MOVE B,SRN4(U)
	PUSHJ P,MFDCK
	 JRST .+2
	  JRST QPNL11
	PUSHJ P,FLDRCK
	 JRST .+2
	  JRST QPNL11
	PUSHJ P,QUDLK
	MOVE Q,QUDFPR(E)
	ADD Q,QSNLCN(H)
	PUSHJ P,QGRLSC	;EITHER NAME > OR <?
	 JRST OPNL11	;BOTH-BARF
	  PUSHJ P,QFNG	;ONE- COMPUTE EFFECTIVE FILE NAME
	MOVE C,QSNUD(H)
	PUSHJ P,MNGDIR	;If RENMWOing in sanctified directory
	 PUSHJ P,SYSDSK	; Tattle.
	MOVE I,Q	;IN CASE GO TO QRNAM4
	LDB TT,[$QAMOD,,QSRAC(E)]
	SKIPL QSCRW(E)	;SKIP IF WRITE BLOCK (WRITE FILE) MAYBE 0 FOR WRITEOVER
	 JUMPE TT,QRNAM4	;JUMP IF READ FILE
	PUSHJ P,QRELOC
	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
	PUSHJ P,QUDULK
	JRST POPJ1

SUBTTL PAW OVER USER DIRECTORY

QSKDP1:	ANDCAB TT,QSNLCN(H)
	PUSH P,A
	PUSH P,B
	PUSH P,C
	PUSH P,D
	PUSH P,I
	PUSH P,W
	MOVE C,UDNAMP(TT)
	ADDI C,(TT)	;ADDR OF BEGINNING OF NAME AREA
	MOVEI D,2000(TT)	;D => FROM POINTER
	MOVE I,D	;I => TO POINTER
	HRLOI E,377777	;PREVIOUS FN1 (BIT 4.9 COMPLEMENTED)
	MOVE W,E	;FN2
	MOVEI J,0	;J NEGATIVE => NEED SORT, NON-ZERO => DIR MODIFIED
QSKDP4:	SUBI I,LUNBLK
QSKDP5:	SUBI D,LUNBLK
	CAMLE C,D
	 JRST QSKDP8	;THROUGH
	SKIPN A,UNFN1(D)
	 SKIPE UNFN2(D)
	  JRST QSKDP3
	HLLOS J
	JRST QSKDP5	;NAME BLOCK FREE

QSKDP3:	MOVE Q,UNRNDM(D)
	TLNN Q,UNIGFL
	 JRST QSKDPR	;NOT OPEN FOR WRITE OR BEING DELETED
	HLLOS J		;NEED TO WRITE OUT DIR
	SKIPN A,UNFN1(D)
	 MOVSI A,(SETZ)
	SKIPN B,UNFN2(D)
	 MOVSI B,(SETZ)
QSKDP7:	PUSHJ P,QGRLSC	;DON'T CREATE ANY FILE WITH NAME OF < OR >
	 AOJA A,QSKDPA	;CHANGE BOTH JUST TO BE SURE
	  AOJA A,QSKDPA
	MOVE Q,QSNLCN(H)	;SEE IF THAT NAME EXISTS
	MOVEI Q,2000-LUNBLK(Q)	;THERE MAY BE DUPLICATE ENTRIES
QSKDPU:	CAMN A,UNFN1(Q)	;DURING THIS SEARCH BUT IT DOESN'T
	 CAME B,UNFN2(Q)	;MATTER
	  JRST QSKDPT
	CAIE Q,(D)	;DON'T CHANGE NAME IF NOT DUPLICATE
QSKDPA:	 AOJA B,QSKDP7	;FOUND IT.  CHANGE SECOND NAME
QSKDPT:	SUBI Q,LUNBLK
	CAMG C,Q
	 JRST QSKDPU
	MOVEM A,UNFN1(D)
	MOVEM B,UNFN2(D)
	MOVSI Q,UNIGFL
	ANDCAM Q,UNRNDM(D)
QSKDPR:	CAMN D,I
	 JRST QSKDP6
	HRLZ Q,D	;RELOCATE FILE BLOCK
	HRR Q,I
	BLT Q,LUNBLK-1(I)
QSKDP6:	JUMPL J,QSKDP4	;ALREADY NEEDS SORT
	MOVE B,UNFN2(D)	;MAY NOT HAVE YET IF GOT HERE FASTEST WAY
	TLC A,(SETZ)
	TLC B,(SETZ)
	EXCH A,E
	EXCH B,W
	CAMLE A,E
	 JRST QSKDP4	;ORDER OK
	CAMN A,E
	 CAMGE B,W
	  MOVNI J,1	;NEED SORT
	JRST QSKDP4

QSKDP8:	ADDI I,LUNBLK	;I POINTED TO EMPTY SLOT
	MOVE D,I
	SUBI D,(TT)
	EXCH D,UDNAMP(TT)
	ADDI D,(TT)
	CAML D,I
	 JRST QSKDPV
	SETZM (D)	;CLEAR VACATED AREA
	HRLS D
	ADDI D,1
	BLT D,-1(I)
QSKDPV:	JUMPL J,QSKDPS	;SORT
	TRNN J,-1
	 JRST QSKDPK
QSKDPX:	MOVE I,QACTB
	IORM I,QSNLCN(H)
QSKDPK:	MOVE W,QSNLCN(H)
IFN QRSRVP,[
	HLRZ A,UDALLO(W)	;IF THIS DIR HAS ALLOCATION
	JUMPE A,QSKBK0
	MOVEI B,NQS-1		;CONVERT PACK # TO DRIVE #
	CAME A,QPKID(B)
	 SOJGE B,.-1
	SKIPGE A,B
QSKBK0:	 SETO A,		;ALLOCATED PACK NOT MOUNTED, USE ANY
	MOVEM A,QSALLO(H)	;SAVE DRIVE # (-1 IF NONE)
];QRSRVP
	HLLZS UDBLKS(W)
	MOVE A,UDNAMP(W)
	ADDI A,(W)
QSKBK1:	CAIL A,2000(W)	;COUNT BLOCKS USED
	 JRST QSKBK2
	MOVE B,UNRNDM(A)
	TLNE B,UNLINK
	 JRST QSKBK3
	LDB B,[UNDSCP+UNRNDM(A)]
	IDIVI B,UFDBPW
	HLL B,QBTBLI(C)
	ADDI B,UDDESC(W)	;B GETS BYTE PNTR TO DESC
QSKBK4:	ILDB C,B
	JUMPE C,QSKBK3
	CAIN C,UDWPH
	 JRST QSKBK4
	CAIG C,UDTKMX
	 JRST [ADDM C,UDBLKS(W) ? JRST QSKBK4]
	CAIG C,UDWPH
	 JRST [AOS UDBLKS(W) ? JRST QSKBK4]
	REPEAT NXLBYT, IBP B
	AOS UDBLKS(W)
	JRST QSKBK4

QSKBK3:	ADDI A,LUNBLK
	JRST QSKBK1

QSKBK2:	POP P,W
	POP P,I
	POP P,D
	POP P,C
	POP P,B
	POP P,A
	JRST QSKDP2

QSKDPS:	MOVEI T,LUNBLK
	ADD P,[3,,3]
QSKDPB:	MOVEI Q,(I)	;BEGINNING OF NAME AREA
	SKIPGE T
	 MOVEI Q,2000-LUNBLK(TT)	;START AT END
	MOVEI W,2000(TT)
	SKIPGE T
	 MOVEI W,-LUNBLK(I)
	SETZM (P)
	TDZA J,J	;0 => SORTED  -1 => MAKE ANOTHER PASS
QSKDPE:	 ADD Q,T
	CAIE Q,(W)
	 JRST QSKDPC
	JUMPE J,QSKDPW
	MOVNS T
	JRST QSKDPB

QSKDPW:	SUB P,[3,,3]
	JRST QSKDPX

QSKDPC:	SKIPN A,UNFN1(Q)
	 SKIPE UNFN2(Q)
	  JRST QSKDPD
	BUG

QSKDPD:	MOVE B,UNFN2(Q)
	TLC A,(SETZ)
	TLC B,(SETZ)
	SKIPE E,(P)	;FIRST ENTRY
	 JRST QSKDPF
QSKDPG:	MOVEM A,-2(P)
	MOVEM B,-1(P)
QSKDPI:	MOVEM Q,(P)
	JRST QSKDPE

QSKDPF:	JUMPL T,QSKDPJ
	CAMLE A,-2(P)
	 JRST QSKDPG
	CAME A,-2(P)
	 JRST QSKDPH
	CAML B,-1(P)
	 JRST QSKDPG
QSKDPH:	REPEAT LUNBLK,[
	EXCH A,.RPCNT(E)
	EXCH A,.RPCNT(Q)
	EXCH A,.RPCNT(E)
]
	MOVNI J,1
	JRST QSKDPI

QSKDPJ:	CAMGE A,-2(P)
	 JRST QSKDPG
	CAME A,-2(P)
	 JRST QSKDPH
	CAMG B,-1(P)
	 JRST QSKDPG
	JRST QSKDPH

SUBTTL OPEN FOR WRITE

;RETURNS WITH QUSR(E) AND QSNLCN(E) ON LSWPR
QWRO2:	JUMPE A,QPNL11
	JUMPE B,QPNL11
	SKIPGE I,EPDL(U)	;PICK UP DESIRED DISK UNIT
	 JRST QWRO2A	;SYSTEMS CHOICE
QWRO2B:	CAIGE I,NQS
	 SKIPE QACT(I)
	  BUG		;WE LOST SOMEHOW...
	MOVE TT,QTUTO(I)
	SKIPL QDPWF(I)	;TUT IN BUT NOT PAWED OVER
	 TLNE TT,40000	;TUT NOT IN
	  PUSHJ P,QTCH1	;READ IN TUT OF DISK INVOLVED
	PUSHJ P,QCHNF	;GET CHANNEL
	PUSHJ P,LOSSET
	    QCHNRT
	MOVE TT,[440700,,5]
	TRNN D,3
	 MOVEM TT,QSBYTE(E) ;UNIT ASCII MODE, BYTE SIZE=7
	PUSHJ P,QUDLK	;LOCK USER DIRECTORY
	PUSHJ P,QGRLSC	;CHECK < AND >
	 JRST QPNLBN	;LOSE
	  PUSHJ P,QFNG	;REPLACE WITH COMPUTED EFFECTIVE NAME
	PUSHJ P,QFREEF	;FIND FREE FILE NAME AREA AND STORE IN QDIRP(E)
	 JRST QFDF	;FILE DIR FULL
	MOVSI TT,UNWRIT	;SET WRITE IN PROGRESS
	IORM TT,UNRNDM(Q)
	MOVE TT,QPKID(I)
	DPB TT,[UNPKN+UNRNDM(Q)]
	PUSHJ P,QAUTH	;SET FILE AUTHOR, MAY NOT PCLSR
	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
	SETOM QSCRW(E)	;SET CHNL WRITE SWITCH
	POPJ P,

QWRO2A:
IFN QRSRVP,[
	SKIPL I,QSALLO(H)
	 JRST QWRO2B	;THIS DIR GOES ON A PARTICULAR UNIT
]
	SKIPGE I,QWRU	;GET CURRENT WRITING UNIT
	 JRST QWRO2E	;NO CURRENT UNIT, GO FIND ONE
	MOVE TT,QSFT(I)
	CAML TT,QFTTHR
	 JRST QWRO2B	;ENOUGH ROOM ON PREFERRED UNIT
QWRO2E:	SETOB T,I	;NOT MUCH SPACE LEFT TRY OTHER UNITS
	MOVSI E,-NQS
QWRO2C:	SKIPGE QACT(E)	;SKIP ON UNIT ACTIVE
	 JRST QWRO2D
IFN QRSRVP,SKIPN QRESRV(E)	;DONT CHOOSE THIS PACK IF RESERVED
	 CAML T,QSFT(E)	;NOTE QSFT IS -1 IF TUT NEVER BEEN READ IN
	  JRST QWRO2D	;OTHERWISE CONTAINS VALID BLOCK COUNT
	MOVE T,QSFT(E)
	HRRZ I,E
QWRO2D:	AOBJN E,QWRO2C
	SKIPGE I
	 BUG		;NO ACTIVE UNRESERVED UNITS
	CAMN I,QWRU
	 JRST QWRO2B	;DON'T WRITE MFD IF UNCHANGED
	MOVEM I,QWRU
	MOVE E,QACTB
	IORB E,QMDRO
	MOVEM I,MPDWDK(E)
	JRST QWRO2B

QWRO:	JUMPN W,QWROR	;REALLY WANT TO MODIFY OR OTHERWISE HACK AN EXISTING FILE
	PUSHJ P,QWRO2	;MAKING NEW FILE, DECIDE WHICH UNIT TO PUT IT ON
	SETOM QMFTP(E)	;TRACK TO SCAN IN TUT
	SETOM QMTTR(E)
	CLEARM QMPTN(E)
	CLEARM QMPTC(E)
	MOVEI TT,%QMWRT	;ENTER NORMAL WRITE MODE
	HRRM TT,QSRAC(E)
	MOVSI TT,%QARWT
	TRNE D,%DORWT/2
	 IORM TT,QSRAC(E)
	JRST QOEX1	;EXIT


SUBTTL	File hacking tracking

; MNGDIR checks the file name (sname in C) 
; Fails to skip if the file is an important system file.
; Skips if the file is of the everyday sort.

MNGDIR:	HLRZ TT,C
IFN KL10P, CAME C,[SIXBIT /.KLFE./]
	CAIN TT,'SYS	;A sys directory?
	 POPJ P,
	CAME C,[SIXBIT /ACOUNT/]
	 CAMN C,[SIXBIT /./]
	  POPJ P,
	CAME C,[SIXBIT /DEVICE/]
	 CAMN C,[SIXBIT /CHANNA/]
	  POPJ P,
	AOS (P)		;Not a system directory. skip return.
	POPJ P,

; SYSDSK notifies the SYS job to print a message on the
; console about the file being hacked.
; File names in C;A B, the opcode in W (or zero) specifies the hacking.

SYSDSK:	JUMPE U,CPOPJ		;Avoid deadly embrace!
	MOVSI T,SCLWRT		;Writing on SYS directory.
	PUSHJ P,CWAIT		;Take turns like nice little lusers.
	    TDNE T,SUPCOR	;Wait for previous req to finish.
	MOVE TT,W		;Check file operation code.
	CAILE TT,4		;If impossible opcode
	 SETZ TT,		; probably supposed to be R/W.
	MOVEM TT,SWMOD		;Store opcode.
	MOVE TT,UNAME(U)
	MOVEM TT,SWUNAM		;Luser.
	MOVE TT,JNAME(U)
	MOVEM TT,SWJNAM
	MOVEM A,SWFN1
	MOVEM B,SWFN2
	MOVEM C,SWFN3		;Sname.
	IORM T,SUPCOR		;Notify the SYS job.
	JRST CLKONJ		;Turn on the clock and return.


SUBTTL DIRECTORY ROUTINES

QFREEF:	PUSH P,A	;Q_PTR TO USER DIR FREE FILE  QDIRP(E)_PTR TO FREE DESC AREA
	PUSH P,B	;GET FREE FILE AREA
	PUSH P,I
	MOVEI I,0	;SIGNAL NO GC YET
QFREFA:	SETZM QUDFPR(E)	;CLEAR SO WILL NOT POINT TO RANDOMNESS IN CASE OF G C
	MOVE TT,QSNLCN(H)
	SKIPL Q,UDESCP(TT)
	 CAIL Q,2000*UFDBPW
	  BUG		;FREE DESC POINTER OUT OF RANGE
	IDIVI Q,UFDBPW
	MOVE C,UDNAMP(TT)
	CAIL Q,-UDDESC-7-LUNBLK(C)
	 JRST QAGARB	;NOT ENOUGH ROOM BETWEEN DESC AND NAME AREAS
	PUSHJ P,QLGLK	;FIND WHERE FILE OUGHT TO GO
	 JRST QFREFF	;DIR WAS EMPTY
	TRNN J,1777
	 JRST QFREFE	;GOES AT END OF DIR
QFREFC:	CAMN A,UNFN1(J)
	 CAME B,UNFN2(J)
	  JRST QFREFE
	MOVE C,UNRNDM(J)
	TLNE C,UNIGFL	;* FILES MUST COME AFTER NON * FILES
	 JRST QFREFE	;OF SAME NAME
	ADDI J,LUNBLK
	CAIGE J,2000(TT)
	 JRST QFREFC
QFREFE:	CAMN Q,J
	 JRST QFREFF	;GOES AT BEGINNING
	PUSHJ P,QINSRT
	JRST QFREFD

QFREFF:	SUBI Q,LUNBLK		;Q -> NAME BLOCK FILE WILL GO IN
QFREFD:	MOVNI T,LUNBLK		;ALLOCATE MORE SPACE FOR NAME AREA
	ADDM T,UDNAMP(TT)
	HRRZ A,UDESCP(TT)	;FIRST FREE DESCRIPTOR LOC
	DPB A,[UNDSCP+UNRNDM(Q)];STORE IN FILE AREA
	MOVEM A,QDIRP(E)	;STORE IN CHANNEL
	MOVEI B,6*UFDBPW+1	;ENOUGH FOR A LINK WITH EVERY CHAR QUOTED PLUS ONE ZERO
	ADDM B,UDESCP(TT)	;ALLOCATE MORE SPACE FOR DESC AREA
	MOVE B,Q
	SUB B,QSNLCN(H)
	HRRZM B,QUDFPR(E)	;ASSOCIATE CHANNEL WITH FILE
	CONO PI,CLKOFF
	SKIPL B,QDATE		;GET TIME AND DATE
	 HRR B,TIMOFF
	CONO PI,CLKON
	MOVEM B,UNDATE(Q)	;SET CREATION DATE
	HLLM B,OLDRDT(E)
	HLRS B
	DPB B,[UNREFD+UNREF(Q)]	;SET REFERENCE DATE
	IDIVI A,UFDBPW		;GET WRD AND CHAR ADRS
	HLLZ B,QBTBLI(B)
	ADDI B,UDDESC(A)
	HRRZ A,QSNLCN(H)
	ADD B,A
	ILDB A,B
	SKIPE A
	 BUG			;DESCRIPTOR AREA ALREADY OCCUPIED
	MOVEI A,UDWPH
	DPB A,B
	ILDB A,B
	SKIPE A
	 BUG			;NOT FOLLOWED BY ZERO
	POP P,I
	POP P,B
	POP P,A
	MOVEM A,UNFN1(Q)	;SET FILE NAMES
	MOVEM B,UNFN2(Q)
	JRST POPJ1

QAGARB:	JUMPL I,QNOFRE	;IF GC WAS ALREADY TRIED, DON'T TRY IT AGAIN - GIVE UP.
	PUSHJ P,QGC	;GC.  BTW, IT IS OK TO PCLSR HERE.
	 JFCL
	MOVNI I,1	;SIGNAL GC HAS BEEN TRIED
	JRST QFREFA

;MAKE ROOM FOR FILE BEFORE J.  Q POINTS TO NEW EMPTY FILE SLOT
QINSRT:	PUSH P,A
	PUSH P,TT
	HRRZ TT,QSNLCN(H)
	HRRZ A,UDNAMP(TT)
	PUSH P,A
	ADDI A,(TT)
	HRLS A
	SUBI A,LUNBLK
	BLT A,-LUNBLK-1(J)
	SETZM -LUNBLK(J)
	HRRZI A,-LUNBLK+1(J)
	HRLI A,-LUNBLK(J)
	BLT A,-1(J)
	SUB J,QSNLCN(H)
	MOVSI A,-NQCHN
QINSR1:	HRRZ TT,QUDPR(A)
	SKIPL QUSR(A)
	CAIE TT,(H)
	JRST QINSR2
	MOVE TT,QUDFPR(A)
	CAMGE TT,(P)
	JRST QINSR2
	CAIGE TT,(J)
	SUBI TT,LUNBLK
	MOVEM TT,QUDFPR(A)
QINSR2:	AOBJN A,QINSR1
	ADD J,QSNLCN(H)
	HRRZI Q,-LUNBLK(J)
	SUB P,[1,,1]
	POP P,TT
	POP P,A
	POPJ P,

;DIR NOT IN CORE.  GET IT FROM DISK, CREATE IT IF DOESN'T ALREADY
; EXIST, OR GIVE NO SUCH DIRECTORY ERROR.
;C HAS SNAME.  RETURNS DIRECTORY NUMBER IN H.

QSKO1:	PUSHJ P,SWTL
	    QSKOSW	;PREVENT TIMING ERROR IF TWO PCS SHOULD
	PUSHJ P,QFLD	; ATTEMPT TO BRING IN SAME DIRECTORY
	 JRST QSKO11
	MOVEI T,2	;SOMEONE ELSE BROUGHT IT IN, RELEASE QSKOSW
	JRST LSWPON	;BUT LEAVE QSNNR(H) LOCKED.

QSKO11:	PUSH P,J
	PUSH P,I
	PUSHJ P,QFL	;LOOK UP DIR IN MFD, RET TRACK IN J
	 JRST QSKON	;NON EXISTENT
	PUSHJ P,QFLDF	;FIND FREE LOSER DIR SLOT
	MOVE I,MDSK
	PUSHJ P,QCHNF	;FIND FREE CHNL (TO READ IN DIR)
	MOVEM C,QSNUD(H)	;SET USER NAME IN DIR SLOT
	MOVEM J,QSLGL(E)	;REQUEST READ IN OF USER DIR FROM TRACK IN J
	MOVEI TT,%QMUDR
	MOVEM TT,QSRAC(E)
QSKON1:	MOVEI T,2
	PUSHJ P,LSWPON	;RELEASE QSKOSW, LEAVE SOSSET OF QSNNR(H)
	POP P,I		;GO AWAY WILL HANG UP WAITING IN QUDLK
	POP P,J		;DIR IS LOCKED BUT NOT ON LSWPR, PI WILL UNLOCK AFTER READIN
	JRST QSTRTR

QNOFRE:	POP P,I		;NO FREE FILES AVAIL
	JRST POPBAJ

QPNL24:	PUSHJ P,OPNL24
	JRST URET

QPNL20:	PUSHJ P,OPNL20
	SKIPE QLD(U)
	 PUSHJ P,OPNL47	;FOUND LINK, BUT IT POINTS TO NON-EXISTENT DIRECTORY
	JRST URET

AUTOCR:			;AUTOMATICALLY CREATED DIRECTORIES
IFN TPLP+UNSPLP,SIXBIT /.LPTR./	;FOR TPL SPOOLING
	SIXBIT /.MSGS./		;FOR MESSAGES TO ALL LOSERS
	SIXBIT /.MAIL./		; Programs like to write mail here
	SIXBIT /CRASH/		; Programs like to dump themselves here
	SIXBIT /.TEMP./		; Programs like to write randomness here
NATOCR==.-AUTOCR

QSKON:	JUMPE C,QPNL20	;DON'T ALLOW ZERO USER NAME
	PUSH P,TT
	MOVSI TT,-NATOCR
QSKONA:	CAMN C,AUTOCR(TT)
	 JRST QSKONB
	AOBJN TT,QSKONA
	CAMN A,[SIXBIT /..NEW./]
	 CAME B,[SIXBIT /(UDIR)/]
	  JRST [POP P,TT
		JRST QPNL20]
	BUG INFO,[DSK: DIR ],SIXBIT,C,[CREATED BY ],SIXBIT,UNAME(U),SIXBIT,JNAME(U) 
QSKONB:	POP P,TT
	SKIPG NQFUS
	 JRST QPNL24	;NO MFD SLOTS AVAILABLE
	PUSH P,A
	PUSH P,B
	PUSH P,C
QSKONE:	PUSHJ P,QFLDF	;FIND FREE DIR SLOT
	PUSHJ P,QMLOCK
	PUSHJ P,TCALL
	 JRST IOMQ
	  JRST [PUSHJ P,LSWPOP	;QMDRO
		PUSHJ P,LSWPOP	;QSNNR
		PUSHJ P,UDELAY	;HOPEFULLY MEMORY WILL APPEAR SHORTLY
		JRST QSKONE]
	MOVEM C,QSNUD(H)
	SOS NQFUS	;NO TIMING ERR DUE TO QSKOSW
	MOVEI J,MU23UD
	DPB J,[MUR,,MEMBLT(A)]
	DPB H,[MNUMB,,MEMBLT(A)]
	LSH A,10.
	HRRM A,QSNLCN(H)
	SETZM (A)
	HRLS A
	AOS B,A
	BLT A,2000-2(B)
	MOVEI A,2000
	MOVEM A,UDNAMP-1(B)
	MOVE A,QSNUD(H)
	MOVEM A,UDNAME-1(B)
	MOVE B,QACTB		;NOW PUT UFD INTO MFD
	IORB B,QMDRO
	MOVE TT,MDNAMP(B)	;LOOK FOR A FREE SLOT
	ADDI TT,(B)
QSKONC:	TRNN TT,1777
	 JRST QSKOND
	SKIPN MNUNAM(TT)
	 JRST QSKONF
	ADDI TT,LMNBLK
	JRST QSKONC

QSKONF:	SUBI TT,(B)
	JRST QSKONG

QSKOND:	MOVNI TT,LMNBLK		;NO FREE SLOTS, SO EXTEND DOWNWARD
	ADDB TT,MDNAMP(B)
QSKONG:	ADDI B,(TT)
	MOVEM A,MNUNAM(B)
	SUBI TT,2000-2*NUDSL	;GET DISK BLOCK NUMBER
IFN KA10P, SKIPGE TT
IFE KA10P, CAIGE TT,2		; Don't clobber 'HOM' blocks
	 BUG			;TOO MANY UFDS (NQFUS CHECK DIDN'T WORK)
	LSH TT,-1
	MOVEM TT,QSNMI(H)	
	MOVE TT,QACTB		;UNLOCK UFD AND CAUSE IT TO GET WRITTEN
	HLLM TT,QSNLCN(H)
	PUSHJ P,QMULK		;UNLOCK MFD
	POP P,C
	POP P,B
	POP P,A
	JRST QSKON1

SUBTTL MAKE LINK

QALINK:	MOVE I,MDSK	;ENTER WITH QSNNR ON LSWPR
	MOVEM I,EPDL(U)
	PUSHJ P,QWRO2	;MAKES NEW FILE WITH MINIMUM OF 37 BYTES OF DESC SPACE
	MOVSI A,UNLINK	; AND ADDS QUSR AND QSNLCN ON LSWPR
	IORM A,UNRNDM(Q)	;SET LINK BIT
	MOVE A,E	;QSK CHANNEL NUMBER
	MOVE C,SRN5(U)	;SNAME LINKED TO
	PUSHJ P,LDEP
	 PUSHJ P,QUDS
	MOVE C,SRN3(U)	;FN1 LINKED TO
	PUSHJ P,LDEP
	 PUSHJ P,QUDS
	MOVE C,SRN4(U)	;FN2 LINKED TO
	PUSHJ P,LDEP
	 JFCL
	MOVE E,A	;QSK CHANNEL NUMBER FOR QCHNRT
	MOVEI R,EPDL(U)	;DON'T CLOSE A REAL IOCHNM WORD
	PUSHJ P,QSOCL4	;CLOSE CHNL & FILE
	PUSHJ P,LSWDEL	;DELETE QUSR, HAS BEEN SETOM'ED
	PUSHJ P,LSWDEL	;DELETE QSNRR, HAS BEEN SOS'ED
	JRST POPJ1

LDEP:	MOVEI E,6	;STORE SIXBIT FROM C INTO LINK DESC
LDEPL:	MOVEI B,0	;GET NEXT CHAR
	LSHC B,6
	JUMPE B,LDEPS	;*THIS ALLOWS EMBEDDED BLANKS.
	CAIE B,';
	 CAIN B,':
	 JRST LDEPS
LDEPS2:	MOVE D,B
	PUSHJ P,QUDS
	SOS E		;NUMBER OF CHARACTERS LEFT IN WORD
	JUMPN C,LDEPL	;JUMP IF ANY MORE NON-BLANK CHARS TO STORE
	MOVEI D,';
	JUMPE E,POPJ1	;JUMP IF STORED 6 CHARACTERS
	POPJ P,		;STORED FEWER, NEED TERMINATOR

LDEPS:	MOVEI D,':	;THIS CHAR NEEDS TO BE QUOTED
	PUSHJ P,QUDS
	JRST LDEPS2

;LINK ENCOUNTERED DURING LOOKUP

QLINK:	TRNE D,%DONLK/2	;CHECK 3.5 BIT IN OPEN MODE
	 JRST QOLINK	;JUMP IF DON'T CHASE LINKS MODE
	AOS A,QLD(U)
	CAIL A,100.
	 JRST OPNL27	;LINK DEPTH EXCEEDED
	PUSH P,E	;SAVE XR NEEDED BY LSWPOP OF QUSR ENTRY
	MOVE E,TT
	IDIVI E,UFDBPW
	ADD E,QSNLCN(H)
	ADDI E,UDDESC
	MOVE TT,QBTBLI(TT)
	HRR TT,E
	MOVE I,[440600,,A]
	SETZB A,B
	SETZ C,
QL1:	ILDB J,TT
	JUMPE J,QL3	;END DESC
	CAIN J,':
	 JRST QL4	;QUOTE NEXT CHAR
	CAIN J,';
	 TLZA I,770000	;TERMINATE THIS WORD
QL5:	  IDPB J,I
	JRST QL1

QL4:	ILDB J,TT	;GET CHAR THAT WAS QUOTED
	JRST QL5

QL3:	EXCH A,C	;END OF DESC REACHED
	EXCH A,B	;MAKE A FN1 B FN2 C SNM
	SKIPN C
	 MOVE C,USYSN1(U)
	PUSHJ P,QUDULK	;UNLOCK DIR
	POP P,E		;RESTORE XR USED BY LSWPOP OF QUSR ENTRY
	PUSHJ P,LSWPOP	;QUSR ENTRY
	PUSHJ P,LSWPOP	;QSNNR ENTRY
	PUSHJ P,OPBRK	;IF TRYING TO BE PCLSRED, SUBMIT
	JRST QSKOL

QOLINK:	MOVSI TT,%QALNK	;OPENING UP A LINK
	IORM TT,QSRAC(E);DON'T ALLOW FILE-ONLY OPERATIONS SUCH AS IOT
	JUMPL D,QWROR1	;LEAVE RH(QSRAC)=%QMIDL SO PI LEVEL WON'T MESS WITH IT
	JRST QOEX1	;AND FINISH OPENING

SUBTTL PAW OVER MFD

QMCH1:	CONO PI,UTCOFF
	MOVE TT,QMDRO
	TLNE TT,40000
	AOJE TT,QMCH2	;NOT ON WAY IN
QMCH3:	CONO PI,UTCON
	SKIPGE QMDRO
	 PUSHJ P,UFLS
	MOVSI E,2
	TDNN E,QMDRO
	 POPJ P,	;PAWED OVER
	PUSHJ P,QMLOCK
	ANDCAM E,QMDRO
	HRRZ E,QMDRO
	MOVE TT,MDCHK(E)
	CAME TT,[SIXBIT /M.F.D./]
	 BUG HALT,[MFD CLOBBERED]
QMCH1A:	MOVEI E,2000-LMNBLK*NUDSL(E)
IFN KA10P, SETZM NQFUS
IFE KA10P,[
	MOVNI TT,2		;PROTECT DEC 'HOM' BLOCKS SO KLDCP OR 8080
	MOVEM TT,NQFUS		; CAN FIND ITS CRUFT
] ;IFE KA10P
QMCH1E:	LDB TT,[1200,,E]
	JUMPE TT,QMULK
	SKIPN (E)
	 AOS NQFUS
	ADDI E,LMNBLK
	JRST QMCH1E

QMCH2:	SOS QMDRO	;INDICATE ON WAY IN
	CONO PI,UTCON
	SKIPG QFCHN
	 BUG		;DON'T WANT TO WAIT FOR CHANNEL?
	PUSHJ P,QCHNF
	MOVEI TT,MFDBLK	;MFD TRACK
	MOVEM TT,QSLGL(E)
	MOVEI TT,%QMMDR
	MOVEM TT,QSRAC(E)
	PUSHJ P,QSTRTR
	JRST QMCH3

SUBTTL PAW OVER TUT

QTCH1:	CONO PI,UTCOFF
	MOVSI TT,200000
	TDNE TT,QTUTO(I)
	 JRST QTCH2	;GO READ IT IN
	CONO PI,UTCON
QTCH1A:	PUSHJ P,QTLOCK	;TO MAKE SURE IT'S IN
	AOSE QDPWF(I)
	 JRST QTULK	;ALREADY PAWED OVER
	PUSH P,A
	PUSH P,B
	MOVE T,QTUTO(I)
	MOVE E,QSWAPA(T)
	IDIVI E,DECADE
	SKIPE TT
	 ADDI E,1
	IMULI E,DECADE	;ROUND SWAPPING ALLOC TO MULTIPLE OF A DECADE
	MOVEM E,QSWAPA(T)
	PUSH P,D
	PUSH P,E	;FIRST TRACK OF NON-SWAPPING (FILE) AREA
	MOVE E,QTUTP(T)	;ROUND TUT POINTER TO MULTIPLE OF A DECADE
	ADDI E,DECADE-1
	IDIVI E,DECADE
	IMULI E,DECADE
	CAMGE E,(P)	;KEEP IT WITHIN THE FILE AREA
	 MOVE E,(P)
	MOVEM E,QTUTP(T)
	MOVE E,QPKNUM(T)
IFN DC10P,[
	CAME E,QPKID(I)
	 BUG HALT,[PACK ID ],OCT,E,[IN TUT FOR UNIT ],DEC,I,[DIFFERS FROM HARDWARE PACK ID],OCT,QPKID(I)
]
IFE DC10P,	MOVEM E,QPKID(I)
	MOVE E,QPAKID(T)
	MOVEM E,QPKNM(I)
IFN QRSRVP,[
	MOVE E,QTRSRV(T)
	MOVEM E,QRESRV(I)
]
;DROPS THROUGH
;DROPS IN
	CLEARM QSFTS(I)	;FREE SPACE IN SWAPPING AREA
	SETZB D,QSFT(I)
	MOVE B,QTUTO(I)
	HRLI B,(TUTBP)
	ADDI B,LTIBLK
QTCH1D:	ILDB A,B
	JUMPN A,QTCH1F
	CAML D,(P)
	 AOS QSFT(I)	;BLOCK IN NON-SWAPPING AREA
	CAMGE D,(P)
	 AOS QSFTS(I)	;BLOCK IN SWAPPING AREA
QTCH1F:	ADDI D,1
	CAMGE D,QLASTB(T)
	 JRST QTCH1D
	SUB P,[1,,1]
	POP P,D
	POP P,B
	POP P,A
	JRST QTULK

QTCH2:	SETOM QDPWF(I)
	ANDCAM TT,QTUTO(I)
	CONO PI,UTCON
	SKIPG QFCHN
	 BUG			;DON'T WANT TO WAIT FOR CHANNEL?
	PUSHJ P,QCHNF
	HRRZM I,QDSKN(E)
	MOVEI TT,MFDBLK		;TELL PI LEVEL TO READ IT ALL IN
	SUB TT,NTBL(I)
	MOVEM TT,QSLGL(E)
	LDB TT,[121000,,QTUTO(I)]
	MOVEM TT,QSCABN(E)
	MOVEI TT,%QMTTR
	MOVEM TT,QSRAC(E)
	PUSHJ P,QSTRTR
	JRST QTCH1A

;ROUTINE TO ACCESS TUT
;CALL WITH DISK UNIT NUMBER IN I, BLOCK NUMBER IN D
;RETURNS IN D AN LDB-STYLE POINTER TO THE TUT AND IN B THE BYTE
;THAT YOU WOULD GET BY LDB'ING THAT POINTER.
;CLOBBERS E
;CALLER MUST LOCK TUT

TUTPNT:	MOVE B,QTUTO(I)
	CAML D,QFRSTB(B)
	CAML D,QLASTB(B)	
	 BUG			;BLOCK NUMBER NOT IN RANGE COVERED BY TUT
	SUB D,QFRSTB(B)
	IDIVI D,TUTEPW
	ADDI D,LTIBLK(B)
	HLL D,TBTBL(E)
	LDB B,D
	POPJ P,

QSTRTR:
IFN DC10P,[
QSTRT1:	CONSO DC0,DSKCHN
	 SETOM QHUNGF		;SOMEONE PUSHED RESET BUTTON, RECOVER AT PI LEVEL
	CONO DC0,DCSET+DCIENB+DSKCHN	;ENABLE IDLE INTERRUPT
]
IFN RP10P+RH10P+RH11P,[
	CONO PI,UTCOFF
	PUSHJ P,QSTRT1
	CONO PI,UTCON
]
	POPJ P,

IFN RP10P+RH10P+RH11P,[	;START DISK WITH UTC ALREADY OFF
QSTRT1:	SETOM QGTBZY	;NOTE: TURNS UTC BACK ON BUT NOT CLOCK
IFN RP10P,	CONSO DPC,20	;ONLY IF DISK IDLE
IFN RH10P,	CONSO DSK,%HIBSY
IFN RH11P,	IORDQ TT,%HRCS1
IFN RH11P,	TRNE TT,%HXRDY
	 CONO PI,DSKRQ
	POPJ P,
]

;TRY TO FLUSH UNNEEDED UFDS FROM CORE.
;ARG IN C IS ROUTINE TO FREE THE MEMORY.
;CALL WITH UDRSW SIEZED, OR WITH PI 2 IN PROGRESS AND UDRSW NOT LOCKED BY ANYONE
;CLOBBERS H, TT.  DOESN'T SKIP.
QDFLS:	MOVSI H,-QNUD
QDFLS1:	SKIPE QSNUD(H)
	 SKIPE QSNNR(H)
QDFLS2:	  AOBJN H,QDFLS1	;CAN'T FLUSH IF SLOT NOT USED OR CHANNELS OPEN IN DIR
	JUMPGE H,CPOPJ
	MOVE TT,QACTB
	TLO TT,600000
	TDNE TT,QSNLCN(H)
	 JRST QDFLS2		;CAN'T FLUSH IF LOCKED OR NOT WRITTEN OUT
	PUSHJ P,QDFCHK		;CHECK THAT NO POINTERS TO THIS DIRECTORY REMAIN
	CLEARM QSNUD(H)		;FLUSH
	AOS QFUD
	PUSH P,A
	HRRZ A,QSNLCN(H)
	LSH A,-10.
	PUSHJ P,(C)		;RETURN THE MEMORY
	POP P,A
	JRST QDFLS2		;TRY FLUSHING SOME MORE

;CALL WITH UFD SLOT# IN H, WHEN YOU THINK THAT UFD IS FREE.
QDFCHK:	PUSH P,C
	PUSH P,J
	MOVSI C,-NQCHN
QDFCH1:	SKIPGE QUSR(C)
	 AOBJN C,QDFCH1
	JUMPGE C,QDFCH2
	HRRZ J,QUDPR(C)
	CAIN J,(H)
	 BUG		;IT WASN'T REALLY FREE, MAYBE QSNNR IS SCREWED UP?
	AOBJN C,QDFCH1
QDFCH2:	POP P,J
	POP P,C
	POPJ P,

;FIND A FREE UFD SLOT, RETURN INDEX IN H.  PUTS QSNNR ON LSWPR.
;CLOBBERS T, TT.

QFLDF:	PUSHJ P,SWTL
	    UDRSW
	MOVSI H,-QNUD
	SKIPLE QFUD
	 JRST QFLDF1
	PUSH P,C	;ALL UFD SLOTS IN USE, TRY FLUSHING SOME
	MOVEI C,MEMR
	PUSHJ P,QDFLS
	POP P,C
	MOVSI H,-QNUD
	SKIPLE QFUD
	 JRST QFLDF1
	PUSHJ P,LSWPOP	;UDRSW
	PUSHJ P,UDELAY	;WAIT 1/2 SECOND THEN TRY AGAIN, INCLUDING QDFLS
	JRST QFLDF

QFLDF1:	SKIPN QSNNR(H)	;SLOT ALREADY GOBBLED
	 SKIPE QSNUD(H)	;OR ALREADY OCCUPIED
	  AOBJN H,QFLDF1 ;MEANS CAN'T TAKE IT
	SKIPL H
	 BUG		;NONE FREE, QFUD OUT OF PHASE WITH REALITY
	HRRZS H
	SOS QFUD
	MOVSI TT,600000	;DOUBLE LOCK
	MOVEM TT,QSNLCN(H)
	AOSG QSNNR(H)
	 BUG
	PUSHJ P,LSWPOP	;UDRSW
	PUSHJ P,LOSSET
	    QFLDRT
	POPJ P,

;LOSSET ROUTINE
QFLDRT:	SKIPL A,AC0S+H(U)
	 CAIL A,QNUD
	  BUG		;H CLOBBERED
	SOSGE T,QSNNR(A)
	 BUG
	JUMPG T,CPOPJ	;HOW DID SOMEONE ELSE GET IT?
	SKIPN QSNUD(A)
	 AOS QFUD	;READ-IN NEVER STARTED, SLOT BECOMES FREE
	POPJ P,		;OK TO LEAVE 600000,, SET IN QSNLCN

;C HAS DIR NAME.
;IF IN CORE, SET H TO UFD SLOT#, AOS QSNNR, PUT ON LSWPR, AND SKIP.
;IF DIRECTORY NOT IN CORE, NO SKIP.  CLOBBERS T,TT.
QFLD:	PUSHJ P,SWTL
	    UDRSW
QFLD1A:	MOVSI H,-QNUD
QFLD1:	CAME C,QSNUD(H)
	 AOBJN H,QFLD1
	JUMPGE H,LSWPOP	;JUMP ON FAILED TO FIND USER.
QFLD2A:	AOSG QSNNR(H)
	 BUG		;MUST HAVE GOTTEN NEGATIVE SOMEHOW
	PUSHJ P,LSWPOP	;UDRSW
;	PUSHJ P,SOSSET
;	    QSNNR(H)
;THIS IS AN ATTEMPT TO FIND A BUG.
	PUSH P,T
	MOVEI T,QSNNR(H)
	MOVEM T,IOTBTS(U)
	PUSHJ P,LOSSET
	    [ SOSGE @IOTBTS(U)
	       BUG
	      POPJ P, ]
	POP P,T
;END TEMPORARY CODE
	JRST POPJ1

;C <=SYS NAME,   RETURNS TRACK ADDR OF DIR IN J, SKIPS IF FINDS LOSER
QFL:	PUSHJ P,QMLOCK
	PUSHJ P,QFL0
	 JRST QMULK
	AOS (P)
	JRST QMULK	

QFL0:	PUSH P,Q
	HRRZ Q,QMDRO
	ADD Q,MDNAMP(Q)	;PTR TO USER AREA
QFL1:	LDB J,[1200,,Q]
	JUMPE J,QFL3
	CAMN C,MNUNAM(Q)
	 JRST QFL2
	ADDI Q,LMNBLK
	JRST QFL1

QFL2:	SUBI J,2000-LMNBLK*NUDSL	;J <= TRACK ADDR OF USER DIR
	LSH J,-1
	AOS -1(P)	;SUCCESS
QFL3:	POP P,Q
	POPJ P,

;ROUTINE TO ASSIGN A DISK CHANNEL.
;ARGS:	U USER TO GO IN QUSR, H UFD SLOT# TO GO IN QUDPR, I DSK# TO GO IN QDSKN
;RETURNS QSK CHNL INDX IN E.
;DOESN'T DO A LOSSET OF QCHNRT, BUT CALLER MAY WANT TO.
;CLOBBERS T.  NEVER SKIPS.

QCHNF:	PUSHJ P,SWTL	;PREVENT ANYONE ELSE FROM ALLOCATING CHANNELS
	    QCHSW
	MOVSI E,-NQCHN
	SKIPLE QFCHN
	 JRST QCH2	;SOME CHANNELS ARE AVAILABLE, GO FIND ONE
	PUSHJ P,LSWPOP	;QCHSW
	SKIPG QFCHN
	 PUSHJ P,UFLS
	JRST QCHNF

QCH2:	SKIPGE QSRAC(E)	.SEE %QALOK
	 JRST QCH3
	SKIPGE QUSR(E)
	 JRST QCH1
QCH3:	AOBJN E,QCH2
	BUG		;WHERE DID THAT FREE CHANNEL GO?

QCH1:	HRRZS E		;CHANNEL ALLOCATED, INITIALIZE VARIABLES
	HRRZM I,QDSKN(E)
	SETOM QSCABN(E)
	SETOM QSGL(E)
	SETOM QSLGL(E)	;IN CASE OF WRITE-OVER ON 0-LENGTH FILE.
	SETZM QBFP(E)
	SETZM QSBFS(E)
	CLEARM QSLBLK(E)
	SETOM QSMDN(E)
	SETZM QSCRW(E)
	SETZM QSBI(E)
	SETZM QSRAC(E)	.SEE %QMIDL
	CLEARM QFBLNO(E)
	SETZM QSMPRC(E)
	SETZM QSMPRP(E)
	MOVE T,[444400,,1]	;ASSUME BYTES=WORDS, WILL BE FIXED LATER
	MOVEM T,QSBYTE(E)
	SETZM QPCLSR(E)
	HRRZM H,QUDPR(E)
	SETZM QUDFPR(E)	;NOT YET SET UP TO ANY PARTICULAR FILE
	MOVEM U,QUSR(E)	;CHANNEL IS NOW IN-USE
	SOSGE QFCHN
	 BUG
	JRST LSWPOP	;QCHSW

;LOSSET ROUTINE TO RETURN TENTATIVELY ASSIGNED QSK CHNL
;E HAD BETTER CONTAIN THE QSK CHANNEL NUMBER AT "ALL" TIMES
QCHNRT:	SKIPL T,AC0S+E(U)
	 CAIL T,NQCHN
	  BUG		;E CLOBBERED
	HRRZ A,U	;LSWPOP MESSES WITH LH(U)
	CAME A,QUSR(T)
	 BUG
	SETOM QUSR(T)
	AOS QFCHN
	POPJ P,

SUBTTL DELETE, RENAME
;COME HERE FOR DELETE, OR RENAME NOT WHILE OPEN, ON DISK.

QRNAM:	SKIPN SRN3(U)
	 JRST QDEL
	PUSH P,A
	PUSH P,B
	SKIPN B,SRN4(U)
	 JRST QPNL11
	MOVE A,SRN3(U)
	PUSHJ P,MFDCK
	 JRST .+2
	  JRST QPNL13
	PUSHJ P,FLDRCK
	 JRST .+2
	  JRST QPNL13
	POP P,B
	POP P,A
	PUSH P,SRN3(U)
	PUSH P,SRN4(U)
	PUSHJ P,QUDLK
	PUSHJ P,QLOOK
	 JRST [	SUB P,[2,,2]
		JRST QROR1C ]	;GIVE FILE NOT FOUND OR FILE LOCKED
	MOVE I,Q
	POP P,B
	POP P,A
	PUSHJ P,QGRLSC
	 JRST QPNLBN	;FILE ALREADY EXISTS
	PUSHJ P,QFNG
QRNAM4:	PUSHJ P,QLOOK
	 JRST QRNAM3
	CAME I,Q	;ALLOW RENAME THAT DOESN'T CHANGE NAMES.
	 JRST QPNL13
QRNAM3:	MOVE Q,I
	MOVSI TT,UNDUMP	;SAY FILE NOT DUMPED.
	ANDCAM TT,UNRNDM(Q)
	MOVNI E,1	;TELL QFREF NOT TO CLOBBER NON-EX DISK CHNL.
	PUSHJ P,QFREF	;"REFERENCE" FILE
	PUSHJ P,QRELOC	;STORE NEW NAMES IN DIR
QRNAM2:	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
QRNAM1:	MOVE U,USER	;U MAY NOT HAVE USER IF CAME HERE FROM LOGOUT
	PUSHJ P,LSWCLR
	JRST POPJ1

QGRLSC:	CAME A,[SIXBIT />/]	;SKIP NONE IF BOTH A+B ARE SPECIAL
	CAMN A,[SIXBIT /</]	;ONCE IF ONE IS
	 SOS (P)
	CAME B,[SIXBIT />/]	;AND TWICE IF NEITHER
	CAMN B,[SIXBIT /</]
	 SOS (P)
	AOS (P)
	JRST POPJ1


;MOVE FILE POINTED TO BY Q SO THAT NEW NAMES A AND B WILL BE IN
;ALPHABETICAL ORDER
QRELOC:	PUSH P,TT
	PUSH P,E
	PUSH P,J
	PUSH P,Q
	PUSH P,D
	PUSH P,W
	PUSH P,R
	PUSHJ P,QLGLK	;SEE WHERE FILE MUST GO
	 BUG		;DIR WAS EMPTY
	HRRZ TT,QSNLCN(H)
QRLOCK:	CAIL J,2000(TT)
	 JRST QRLOCJ
	CAMN A,UNFN1(J)
	CAME B,UNFN2(J)
	 JRST QRLOCJ
	MOVE Q,UNRNDM(J)
	TLNE Q,UNIGFL
	 JRST QRLOCJ
	ADDI J,LUNBLK
	JRST QRLOCK

QRLOCJ:	HRRZ Q,-3(P)	;WHERE FILE IS NOW
	CAIE J,LUNBLK(Q)
	 CAMN J,Q
	  JRST QRLOCS	;SAME PLACE
REPEAT LUNBLK,	PUSH P,.RPCNT(Q)
	CAMG Q,J
	 JRST QRLOCB
	MOVEI TT,(Q)
QRLOCC:	SUBI TT,LUNBLK
	HRLZ D,TT
	HRRI D,LUNBLK(TT)
	BLT D,2*LUNBLK-1(TT)
	CAILE TT,(J)
	 JRST QRLOCC
QRLOCA:
REPEAT LUNBLK,	POP P,LUNBLK-.RPCNT-1(J)
	MOVEM A,UNFN1(J)
	MOVEM B,UNFN2(J)
	HRRZ TT,QSNLCN(H)
	SUB Q,TT
	SUB J,TT
	CAML J,Q
	 JRST QRLOCD
	MOVEI D,LUNBLK
	HRRZ W,J
	HRRZ R,Q
QRLOCE:	MOVSI TT,-NQCHN
QRLOCF:	HRRZ E,QUDPR(TT)
	SKIPL QUSR(TT)
	 CAIE E,(H)
	  JRST QRLOCG
	HRRZ E,QUDFPR(TT)
	CAIN E,(Q)
	 JRST QRLOCI
	CAIL E,(W)
	 CAIL E,(R)
	  JRST QRLOCG
	ADD E,D
	HRRZM E,QUDFPR(TT)
QRLOCG:	AOBJN TT,QRLOCF
QRLOCH:	POP P,R
	POP P,W
	POP P,D
	POP P,Q
	POP P,J
	POP P,E
	POP P,TT
	POPJ P,

QRLOCB:	HRRZ D,Q
	HRLI D,LUNBLK(Q)
	BLT D,-1-LUNBLK(J)
	SUBI J,LUNBLK
	JRST QRLOCA

QRLOCD:	MOVNI D,LUNBLK
	HRRZI W,LUNBLK(Q)
	HRRZI R,LUNBLK(J)
	JRST QRLOCE

QRLOCI:	HRRZM J,QUDFPR(TT)
	JRST QRLOCG

QRLOCS:	MOVEM A,UNFN1(Q)
	MOVEM B,UNFN2(Q)
	JRST QRLOCH

QDEL:	PUSHJ P,QUDLK
	PUSHJ P,QLOOK
	 JRST QDFNF
	MOVE TT,UNRNDM(Q)
	TLNE TT,UNLINK
	 JRST QDEL5A
	LDB J,[UNPKN+UNRNDM(Q)]
QDELTA:	MOVSI I,-NQS
	CAME J,QPKID(I)
	 AOBJN I,.-1
	JUMPGE I,QPKNF6	;PACK OF FILE NOT MOUNTED
QDEL5:	PUSHJ P,QUDULK
	PUSHJ P,QDELA
	 JRST QDFNF	;GONE AWAY BETWEEN LOCKS
	JRST QRNAM1

QDEL5A:	;DELETE LINK
	MOVE I,MDSK	;RANDOM EXISTANT UNIT
	JRST QDEL5

QPKNF6:	PUSHJ P,QUDULK
	PUSHJ P,QPKNFP	;MAYBE JUST TUT NOT IN YET?
	 JRST QDEL	;YUP, GOT IT NOW, TRY AGAIN
	PUSHJ P,QUDLK	;NO, FILE IS ON NOT-MOUNTED PACK
	PUSHJ P,QLOOK	;SO GET IT AGAIN
	 JRST QDFNF	;GONE AWAY BETWEEN LOCKS, LOSE
			;AND DELETE IT WITHOUT HACKING TUT
QDFPK:	LDB A,[UNDSCP+UNRNDM(Q)]	;POINTER TO DESCRIPTION AREA
	PUSHJ P,QSQSH	;REMOVE ENTRY
	IDIVI A,UFDBPW
	ADD A,QSNLCN(H)
	ADDI A,UDDESC
	HLL A,QBTBLI(B)
	MOVEI C,0
QNFDL2:	ILDB B,A
	DPB C,A
	TRNE B,40
	 JRST QNFDL3
	JUMPN B,QNFDL2
	JRST QRNAM2

QNFDL3:	REPEAT NXLBYT,[IDPB C,A
]
	JRST QNFDL2

IFN QAUTHP,[
QAUTH:	PUSH P,B	;STORE UFD INDEX OF FILE'S CREATOR
	PUSH P,C	;THIS IS NOT ALLOWED TO PCLSR.  WE SEARCH THE MFD
	PUSH P,J	;WITHOUT LOCKING IT WHICH SHOULD BE ALL RIGHT SINCE
	PUSH P,I	;UFD'S DON'T MOVE AROUND AND THE MFD IS WIRED IN 1 PLACE IN CORE.
	MOVE J,QSNMI(H)
	MOVE C,UNAME(U)
	CAMN C,QSNUD(H)
	 JRST QAUTH1	;UNAME = SNAME
	PUSHJ P,QFL0
	 SKIPA 		;NOT FOUND IN MFD
	  JRST QAUTH1
	SETZM C
	MOVEI J,5	;STRIP OFF ANY NUMERICS
	MOVE TT,[440600,,UNAME(U)]
	MOVE I,[440600,,C]
	ILDB B,TT
	IDPB B,I
QAUTH2:	ILDB B,TT
	CAIG B,'Z
	CAIGE B,'A
	 JRST QAUTH3
	IDPB B,I
	SOJG J,QAUTH2
	SKIPA		;NO NON-LETTERS

QAUTH3:	PUSHJ P,QFL0
	 SKIPA C,HSNAME(U)	;CAN'T CONVERT UNAME IN ANY FORM, TRY HSNAME AS LAST RESORT
	  JRST QAUTH1
	PUSHJ P,QFL0
	 SETOM J		;CAN'T ENCODE AUTHOR AT ALL
QAUTH1:	DPB J,[UNAUTH+UNREF(Q)]
	POP P,I
	POP P,J
	POP P,C
	POP P,B
	POPJ P,
]		;END IFN QAUTHP
.ELSE QAUTH==CPOPJ

;Q=0 => LOOK UP FILE TO DELETE
;Q .NE. 0 => DELETE FILE POINTED TO BY QUDFPR OF CHNL IN D
;H MUST HAVE DIR SLOT INDEX
QDELA:	MOVEI Q,0
QDLA1:	PUSHJ P,SLUGH
	    QCHSW
	    1000,,QSNLCN(H)
	    1000,,QTUTO(I)
	JUMPN Q,QDLA2
	PUSHJ P,QLOOK
	 JRST QDEL4A	;FNF
	SETO D,		;NO ASSOCIATED CHANNEL
QDLA3:	LDB A,[UNLNKB+UNRNDM(Q)]
	LDB TT,[UNPKN+UNRNDM(Q)]
	CAME TT,J
	 JUMPE A,QDELA1	;ON DIFFERENT DISK (DIDN'T LOCK RIGHT TUT TRY AGAIN)
	MOVE A,Q
	SUB A,QSNLCN(H)
	MOVSI J,-NQCHN
QDEL1:	CAIN D,(J)
	 JRST QDEL2	;IGNORE CHANNEL (IF ANY) WHOSE CLOSING CAUSED THIS
	HRRZ TT,QUDPR(J)
	SKIPL QSCRW(J)	;ONLY LOOK AT READ CHNLS
	 CAIE TT,(H)
	  JRST QDEL2
	HRRZ TT,QUDFPR(J)
	SKIPL QUSR(J)
	 CAIE TT,(A)
	  JRST QDEL2
	MOVSI TT,%QADEL		;FOUND CHNL WITH THIS FILE OPEN
	IORM TT,QSRAC(J)	;DELETE FILE WHEN THIS CHNL CLOSED
	MOVSI TT,UNCDEL		;SET DELETED BIT
	IORM TT,UNRNDM(Q)
QDEL4:	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
	MOVE TT,DCHBT(I)
	IORM TT,QTUTO(I)
QDEL4B:	PUSHJ P,QTULK
	PUSHJ P,QUDULK
	PUSHJ P,LSWPOP
	JRST POPJ1

QDLA2:	MOVE Q,QUDFPR(D)	;COMPUTE FILE ADR FROM CHNL IN D
	ADD Q,QSNLCN(H)	;DIR MAY HAVE MOVED
	JRST QDLA3

QDEL4A:	SOS (P)
	JRST QDEL4B

QCDLWO:	PUSH P,R	;OUTPUT CLOSE BUT FILE WAS DELEWO'ED
	SETZM QSCRW(D)	;FAKE OUT ERROR CHECK AT QSQSH6
	PUSHJ P,QUDULK
QSICLD:	MOVE H,QUDPR(D)	;ENTRY FROM INPUT CLOSE, FILE WAS DELETED
	MOVE I,QDSKN(D)
	PUSHJ P,QUDLK
	MOVE Q,QSNLCN(H)
	ADD Q,QUDFPR(D)
	MOVE J,QPKID(I)
	PUSHJ P,QUDULK
	PUSH P,D
	PUSHJ P,QDLA1
	 BUG		;FILE ISNT THERE?
	POP P,D
	MOVE H,QUDPR(D)	;DIRECTORY NUMBER
	SETOM QUSR(D)	;FREE THE DISK CHANNEL
	AOS QFCHN
	JRST QICLX

QDELA1:	PUSHJ P,QDEL4B	;NOT OUR DISK IN J
	 JFCL
	MOVE J,TT	;DISK ITS REALLY ON
QDELA2:	MOVSI I,-NQS	;TRANSLATE PACK # IN J TO DRIVE # IN I
	CAME J,QPKID(I)
	  AOBJN I,.-1
	JUMPGE I,QPKNF1
	JRST QDELA

QSOCLD:	PUSH P,D	;DELETE FILE WHEN CLOSING OVER IT
	PUSH P,R
	LDB J,[UNPKN+UNRNDM(Q)]
	PUSHJ P,QUDULK
	PUSHJ P,QDELA2
	 JFCL
QPKNF7:	POP P,R
	POP P,D
	POPJ P,

QPKNF1:	PUSHJ P,QPKNFP	;MAYBE TUT NOT IN YET?
	 JRST QDELA2	;YUP, TRY AGAIN
	PUSHJ P,QUDLK
	PUSHJ P,QLOOK
	 JRST QUDULK	;GONE AWAY BETWEEN LOCKS
	JRST QDFPK	;GO DELETE OFF OF NON-MOUNTED PACK

QDL2:	PUSHJ P,QTULK	;DELETE LINK
	MOVEI T,2
	PUSHJ P,LSWPON	;UNLOCK QCHSW
QDLINK:	LDB E,[UNDSCP+UNRNDM(Q)]
	IDIVI E,UFDBPW
	ADD E,QSNLCN(H)
	ADDI E,UDDESC
	HLL E,QBTBLI(TT)
	ILDB A,E
	SKIPN E
	 BUG		;NULL LINK?
	MOVEI B,0
QDL1:	DPB B,E			;CLEAR OUT THE CHAR
	CAIN A,':
	 IDPB B,E		;CLEAR A QUOTED CHAR WITHOUT LOOKING AT IT
	ILDB A,E
	JUMPN A,QDL1
	PUSHJ P,QSQSH	;REMOVE ENTRY
	MOVE TT,QACTB
	IORM TT,QSNLCN(H)
	PUSHJ P,QUDULK
	JRST POPJ1

QDEL2:	AOBJN J,QDEL1		;CHECK NEXT DISK CHANNEL
	MOVE C,UNRNDM(Q)	;NOT OPEN, SO REALLY DELETE IT
	TLNE C,UNLINK
	 JRST QDL2
	ANDI C,.BM UNDSCP
	IDIVI C,UFDBPW
	ADD C,QSNLCN(H)
	ADDI C,UDDESC
	HLL C,QBTBLI(D)		;GET DESCRIPTOR POINTER IN C AND TT
	MOVE TT,C
	MOVEI A,0
	PUSHJ P,NFLLN1		;A GETS NUMBER OF BLOCKS IN FILE
	PUSH P,A
	SKIPN D,QSFBT(H)	;SEE IF ENOUGH QSFBT STORAGE EXISTS
	 JRST QDEL11
QDEL10:	HLRE B,(D)		;NUMBER OF FREE LOCATIONS IN THIS PAGE
	ADD A,B			;DECREASE LOCATIONS NEEDED
	MOVE D,1(D)		;CDR
	JUMPN D,QDEL10
QDEL11:	JUMPLE A,QDEL12		;JUMP IF SUFFICIENT STORAGE EXISTS
	PUSHJ P,TCALL		;GET MORE
	    JRST IOMQ
	 JRST [	MOVE U,USER	;U MAY NOT HAVE USER IF CAME FROM LOGOUT
		PUSHJ P,LSWCLR	;HAVE TO WAIT FOR MEMORY
		PUSHJ P,MQTEST	;AVOID DEADLOCKS BY UNLOCKING EVERYTHING
		 PUSHJ P,UFLS
		JRST UUOTRO ]
	MOVEI D,MU23FB
	DPB D,[MUR,,MEMBLT(A)]
	LSH A,10.
	MOVEI D,2(A)
	HRLI D,-1776
	MOVEM D,(A)
	MOVE D,A
	EXCH A,QSFBT(H)		;ADD TO FRONT OF LIST
	MOVEM A,1(D)
	MOVE A,(P)		;GET BACK NUMBER OF BLOCKS NEEDED
	JRST QDEL10		;SEE IF THERE ARE ENOUGH NOW

QDEL12:	POP P,A			;GET RID OF BLOCKS COUNT
	PUSHJ P,QSQSH		;REMOVE ENTRY, CANNOT PCLSR AFTER THIS
	CLEARB J,D		;J GETS NUMERIC TRACK NUMBER, D BYTE POINTER TO TUT
	MOVE TT,QTUTO(I)	;D ZERO SO WILL HALT IF DESC DOESN'T START WITH LOAD-ADDR!
	MOVE Q,QSFBT(H)		;Q -> AOBJN POINTER FOR STORING BLOCK NUMBERS
QDEL3:	ILDB B,C		; INTO QSFBT AS DESCRIPTORS ARE ZEROED OUT
	MOVEI A,0		;I DISK, C BP TO DESCRIPTOR, A,B,E TEMP.
	DPB A,C
	JUMPE B,QDEL4		;END OF FILE DESCRIPTION
	TRNE B,40
	 JRST QDEL6
	CAILE B,UDTKMX
	 JRST QDEL7
QDEL8:	SKIPN Q
	 BUG			;OOPS, OUT OF QSFBT STORAGE
	SKIPL E,(Q)		;PICK UP AOBJN POINTER
	 JRST [	MOVE Q,1(Q)	;THIS PAGE FULL, TRY NEXT
		JRST QDEL8 ]
	MOVEM J,(E)		;STORE DISK,,BLOCK FOR LATER FREEING
	AOBJN E,.+1
	MOVEM E,(Q)		;INCREMENT POINTER
	MOVE E,QSNLCN(H)	;DECREASE DIR'S BLOCKS-USED
	HRRZ A,UDBLKS(E)
	SOSL A
	 HRRM A,UDBLKS(E)
	ILDB A,D		;CHECK TUT
	SKIPN A
	 BUG			;TUT SHOWS TRACK NOT USED
	AOS J			;DO NEXT BLOCK IN CONTIGUOUS GROUP
	SOJG B,QDEL8
	JRST QDEL3

IFN NXLBYT-2, .ERR THIS ROUTINE AND OTHERS KNOW IMPLICITLY THAT NXLBYT=2
QDEL6:	MOVEI D,0		;LOAD ADDRESS
	DPB B,[140500,,D]
	ILDB B,C
	DPB B,[060600,,D]
	DPB A,C			;A HAS ZERO FROM QDEL3
	ILDB B,C
	DPB B,[0600,,D]
	DPB A,C
	MOVE J,D
	HRL J,I
	PUSHJ P,TUTPNT
	ADD D,[TUTBYT_14,,]	;MAKE INTO ILDB-TYPE POINTER
	JRST QDEL7A

QDEL7:	CAIN B,UDWPH
	 JRST QDEL3
	SUBI B,UDTKMX
	ADD J,B
	IBP D
	SOJG B,.-1
QDEL7A:	MOVEI B,1	;LOAD ADDRESS OR SKIP AND TAKE, SO ONLY ONE TRACK
	JRST QDEL8

QDFNF:	PUSHJ P,OPNL4
	JRST URET

QDELB:	CONO PI,UTCON
	JRST QDELA

;PERFORM BLOCK-FREEING SPECIFIED BY QFBTS
QDLFBT:	CONO PI,UTCOFF		;PROTECT QFBTS
	SKIPN B,QFBTS
	 JRST UTCONJ
	MOVE T,1(B)
	MOVEM T,QFBTS
	CONO PI,UTCON
	HRRZ T,(B)		;FIRST FREE LOCATION
	SUBI T,2(B)		;NUMBER OF LOCATIONS TO DO
	HRLO A,T
	EQVI A,1(B)
	AOBJP A,QDLFB9		;A NOW -> BLOCKS TO FREE, & CHECK FOR EMPTY
	SETO I,			;NO TUT LOCKED YET
QDLFB1:	HLRZ J,(A)		;DISK NUMBER
	CAMN J,I		;MAKE SURE RIGHT TUT IS LOCKED
	 JRST QDLFB2
	SKIPL I
	 PUSHJ P,QTULK
	MOVE I,J
	PUSHJ P,QTLOCK
QDLFB2:	HRRZ D,(A)		;BLOCK NUMBER TO FREE
	PUSHJ P,TUTPNT
	CAIGE B,TUTMNY		;USED IN "MANY" FILES(1,2,...,TUTMNY,TUTLK)
	 SOJL B,[JRST 4,.]	;HALT IF TUT SHOWS TRACK NOT USED
	JUMPN B,QDLFB3		;NOT LAST USE
	HRRZ T,(A)		;BLOCK NUMBER AGAIN
	MOVE TT,QTUTO(I)
	CAML T,QSWAPA(TT)
	 AOSA QSFT(I)
	  AOS QSFTS(I)		;TRACK IN SWAPPING AREA
QDLFB3:	DPB B,D			;DECREASE USAGE COUNT IN TUT
	AOBJN A,QDLFB1
	PUSHJ P,QTULK
QDLFB9:	MOVEI A,-1(A)		;GET AN ADDRESS ON THAT PAGE
	LSH A,-10.		;DONE WITH THIS PAGE, FREE IT
	PUSHJ P,TMEMR		;RETURN PAGE TO FREE AND TRY FOR MORE
	    MU23FB
	JRST QDLFBT

;REMOVE HOLE FROM NAME AREA AT Q
QSQSH:	PUSH P,A
	PUSH P,B
	PUSH P,C
	PUSH P,TT
	HRRZ TT,QSNLCN(H)
	MOVE A,UDNAMP(TT)
	CAILE A,2000
	 BUG		;UDNAMP BAD
	ADDI A,(TT)
	HRRZ C,Q
	SUB C,A
	SKIPL C
	CAIL C,2000-UDDESC
	 BUG		;Q BAD
	HRRZ C,Q
QSQSH1:	SUBI C,LUNBLK
	CAMLE A,C
	 JRST QSQSH2
	HRLZ B,C
	HRRI B,LUNBLK(C)
	BLT B,2*LUNBLK-1(C)
	JRST QSQSH1

QSQSH2:
REPEAT LUNBLK,SETZM .RPCNT(A)
	SUBI A,-LUNBLK(TT)
	HRRZM A,UDNAMP(TT)
	HRRZ C,Q
	SUBI C,(TT)	;INDEX OF DELETED FILE
	HRRZI TT,-LUNBLK(A)	;INDEX OF OLD BEGINNING OF NAME AREA
	MOVSI A,-NQCHN
QSQSH3:	HRRZ B,QUDPR(A)
	SKIPL QUSR(A)
	 CAIE B,(H)
	  JRST QSQSH4
	SKIPN B,QUDFPR(A)
	 JRST QSQSH4
	CAIGE B,(TT)
	 BUG
	CAIN B,(C)
	 JRST QSQSH6	;PNTR TO FLUSHED HOLE, CHECK IF READ CHNL
	CAIG B,(C)	;IF AFTER DELETED FILE DON'T RELOCATE
	 ADDI B,LUNBLK
	HRRZM B,QUDFPR(A)
	SKIPL QSCRW(A)
	 JRST QSQSH4	;READ CHNL
	ADD B,QSNLCN(H)
	MOVE B,UNRNDM(B)
	TLNN B,UNWRIT
	 BUG		;CHNL WRITING BUT BEING WRITTEN BIT NOT SET
QSQSH4:	AOBJN A,QSQSH3
	POP P,TT
	POP P,C
	POP P,B
	POP P,A
	POPJ P,

QSQSH6:	MOVE B,QSRAC(A)	;FOUND PNTR TO DELETED FILE
	TLNE B,%QADEL	;SKIP IF NOT DELETE AFTER CLOSE
	 SKIPGE QSCRW(A) ;ALSO ERROR IF NOT READ CHNL
	  BUG
	JRST QSQSH4	;WAS DELETE AFTER CLOSE ON READ CHNL

SUBTTL DIRECTORY LOOK UP, > FEATURE

;A contains the FN1 and B contains the FN2.
;H is the index of the directory slot.  The dir must be locked.

;Returns the address of the filename block in Q.
;Clobbers C.

;Internally, J and Q point to the bottom and top of the
;area of the dir we are still searching.
;Bit 4.9 of J is set to indicate one of the names is ">".
;C used as a flag: sign bit says creating a file.
;Bit 1.1 of C says FN2 is > or <.

;Look for place to create new file,
;and generate new version for >.
QFNG:	MOVEM Q,EPDL3(U)
	SKIPA C,[SETZ]
;Look for existing files only
QLOOK:	 MOVEI C,0
	PUSH P,J
	HRRZ J,QSNLCN(H)
	MOVEI Q,2000-LUNBLK(J)
	ADD J,UDNAMP(J)
	CAMN A,[SIXBIT />/]
	 TLOA J,400000
	  CAMN A,[SIXBIT /</]
	   JRST QLOOKA	;4.9 BIT OF J SET IF >
	CAMN B,[SIXBIT />/]
	 TLOA J,400000
	  CAMN B,[SIXBIT /</]
	   AOJA C,QLOOK1
	PUSHJ P,QLGLK
	 JRST POPJJ	;FNF
	TRNN J,1777
	 JRST POPJJ	;J IS OFF THE END OF THE BLOCK
	PUSH P,C
	EXCH Q,J
QLK1:	CAMN A,UNFN1(Q)
	 CAME B,UNFN2(Q)
	  JRST QLK3	;FNF
	MOVE C,UNRNDM(Q)
	TLNN C,UNIGFL	;BEING WRITTEN OR DELETED
	 JRST QLK2	;FOUND IT
	SUBI Q,LUNBLK	;SEARCH THROUGH * FILES
	CAML Q,J
	 JRST QLK1
QLK3:	POP P,C
	JRST POPJJ

QLK2:	AOS -2(P)
	JRST QLK3

QFNF2:	SUB P,[2,,2]
	JRST OPNL4

	;REFERENCE FILE POINTED TO BY Q
QFREF:	LDB C,[UNREFD+UNREF(Q)]
	CAME E,[-1]	;IF A DISK CHNL IN USE,
	 HRLM C,OLDRDT(E) ;SAVE OLD REF DATE IN DSK CHNL VAR
	SKIPGE T,QDATE		; If date unknown
	 POPJ P,		; don't clobber
	HLRZ T,T		; Just get date part
	CAMN C,T		; If same as old date
	 POPJ P,		; no need to do anything
	DPB T,[UNREFD+UNREF(Q)]
	MOVE T,MDSK	;MAKE MDSK WRITE OUT
	MOVE T,DCHBT(T)
	IORM T,QSNLCN(H)
	POPJ P,

QLOOKA:	CAME B,[SIXBIT /</]
	CAMN B,[SIXBIT />/]
	 JRST POPJJ	;MUST BE READ RETN FILE NOT FOUND
QLOOK1:	JUMPGE C,QLOOK9
	CAMN B,[SIXBIT /</]
	 TLO J,400000	;IF WRITING, TURN "<" INTO ">"
QLOOK9:	PUSH P,D
	PUSH P,TT
	PUSH P,I
	PUSH P,[-1]	;BEST INDEX
	PUSH P,[SETZ]	;BEST "NUMERIC" PART
	PUSH P,[SETZ]	;BEST ALPHA PART
QLOOK4:	CAIGE Q,(J)
	 JRST QLOOK2
	XCT QLKI1(C)
	 JRST QLOOK3
	MOVE D,UNRNDM(Q)
	TLNE D,UNIGFL
	 JUMPGE C,[ SKIPGE -2(P)	;FILE LOCKED, REMEMBER IT IF WE HAVE NO BETTER,
		     HRRM Q,-2(P)	; BUT DON'T CONSIDER IT IF READING
		    JRST QLOOK3 ]	;BUT IF WRITING, CONSIDER ALL FILES
	SKIPE TT,@QLKI1+1(C)
QLOOK6:	 TRNE TT,77	;RIGHT ADJ
	  JRST QLOOK5
	LSH TT,-6
	JRST QLOOK6

QLOOK5:	MOVEI I,0
QLOOK8:	LDB D,[600,,TT]
	CAIL D,'0
	CAILE D,'9
	 JRST QLOOK7	;NOT A DIGIT
QLOK5B:	TRNE I,77	;RIGHT ADJ LOW NON NUM PART
	 JRST QLOK5A
	LSH I,-6
	JUMPN I,QLOK5B
QLOK5A:	TLC TT,400000	;AVOID CAM LOSSAGE
	TLC I,400000
	SKIPGE -2(P)
	 JRST QLOK5D	;FIRST MATCH
	JUMPGE J,QLOK5E	;GET LEAST
	CAMGE TT,-1(P)	;GET GREATEST
	 JRST QLOOK3
	CAME TT,-1(P)
	 JRST QLOK5D
	CAMGE I,(P)
	 JRST QLOOK3	;NOT AS GOOD
QLOK5D:	HRRZM Q,-2(P)
	MOVEM TT,-1(P)
	MOVEM I,(P)
QLOOK3:	SUBI Q,LUNBLK
	JRST QLOOK4

QLOK5E:	CAMLE TT,-1(P)
	 JRST QLOOK3
	CAME TT,-1(P)
	 JRST QLOK5D
	CAMLE I,(P)
	 JRST QLOOK3
	JRST QLOK5D

QLOOK7:	LSHC TT,-6	;LOW DIGIT NOT NUMERIC
	JUMPN TT,QLOOK8	;NO NUMERIC DIGITS AT ALL ("BIN", MAYBE?)
	JUMPL J,QLOK5B	;IF LOOKING FOR GREATEST, LET THIS BE LEAST
	MOVNI TT,1	;GREATEST IF LOOKING FOR LEAST
	JRST QLOK5B

QLOOK2:	JUMPL C,QFNG1	;REALLY WANT TO MAKE F.N.'S FOR WRITE
	SUB P,[1,,1]
	POP P,C	;BEST "NUMERIC" PART
	POP P,Q	;ADR
	POP P,I
	POP P,TT
	POP P,D
	AOJE Q,POPJJ
	MOVE A,UNFN1-1(Q)	;ACTUAL MATCHED FILE NAMES
	MOVE B,UNFN2-1(Q)
	SOJGE Q,POPJJ1		;FOUND A FILE THAT WASN'T LOCKED
	MOVEI Q,-LUNBLK(Q)
	JRST POPJJ

QFNG1:	SKIPGE -2(P)
	 JRST QFNG2	;NOT FOUND START W/ 1
	MOVE TT,-1(P)
	TLC TT,400000
	MOVE I,[600,,TT]
QFNG3:	LDB D,I
	CAIL D,'0
	CAILE D,'9
	 JRST QFNG4	;REACH END OF NUMERIC FIELD
	AOS D
	CAILE D,'9
	 JRST QFNG5
	DPB D,I
QFNG5A:	TLNE TT,770000
	 JRST QFNG3A
	LSH TT,6
	JRST QFNG5A

QFNG2:	MOVSI TT,(SIXBIT /1/)
QFNG3A:	MOVEM TT,A(C)	;STORE INTO A OR B AS APPRO
	SUB P,[3,,3]
	POP P,I
	POP P,TT
	POP P,D
	MOVE Q,EPDL3(U)
	JRST POPJJ

QFNG5:	MOVEI D,'0
	DPB D,I
	ADD I,[60000,,]
	JUMPL I,QFNG5A
	JRST QFNG3

QFNG4:	TLNN TT,770000	;SKIP ON ALREADY 6 CHAR NAME
	 LSH TT,6
	MOVEI D,'1
	DPB D,I
	MOVEI D,'0
QFNG4B:	TLNN I,770000
	 JRST QFNG5A
	IDPB D,I
	JRST QFNG4B

QLKI1:	CAME B,UNFN2(Q)
	CAME A,UNFN1(Q)
	UNFN2(Q)

QPKNF:	MOVE C,QSNUD(H)
	PUSHJ P,QUDULK	;UNLOCK DIR
	PUSHJ P,LSWPOP	;QUSR ENTRY
	PUSHJ P,LSWPOP	;QSNNR ENTRY
	PUSHJ P,QPKNFP
	 JRST QSKOL
	PUSHJ P,OPNL16	;PACK NOT MOUNTED
	JRST URET

QPKNFP:	MOVSI I,-NQS
QPKNF3:	SKIPGE QACT(I)
	 JRST QPKNF4
	MOVE TT,QTUTO(I)
	SKIPL QDPWF(I)
	 TLNE TT,40000
	  JRST QPKNF2	;PACK NOT IN OR NOT PAWED OVER
QPKNF4:	AOBJN I,QPKNF3
	JRST POPJ1

QPKNF2:	JRST QTCH1	;MAYBE THIS UNIT HAS GOODIES

QFDF:	PUSHJ P,OPNL5	;FILE DIR FULL ON WRITE
	JRST URET

QPNL13:	PUSHJ P,OPNL13
	JRST URET

QPNL22:	PUSHJ P,OPNL22
	JRST URET

QPNLBN:
QPNL11:	PUSHJ P,OPNL11
	JRST URET

;ROUTINE TO FIND PLACE IN DIRECTORY WHERE A B WOULD GO
;SKIPS ONLY IF DIRECTORY CONTAINS AT LEAST ONE FILE
;FOR INSERTION, FILE GOES BEFORE PNTR RETURNED IN J
;RETURNS PNTR IN Q TO BEGINNING OF NAME AREA
;(ONLY WORKS FOR LUNBLK = 5)
QLGLK:	HRRZ J,QSNLCN(H)
	HRRZ Q,UDNAMP(J)
	ADDI Q,(J)
	CAIL Q,2000(J)
	 POPJ P,	;DIRECTORY EMPTY
	TLC A,(SETZ)
	TLC B,(SETZ)
	PUSH P,D
	PUSH P,E
	ADDI J,600	;128. NAME BLOCKS FROM END
REPEAT 7,[		;THIS CODE DELIBERATELY NOT INDENTED. NEED 4 DIMENSIONS.
	CAMGE J,Q
	JRST .+6
	MOVE D,UNFN1(J)
	TLC D,(SETZ)
	CAMN A,D
	JSP E,QLGLE
	CAML A,D
	ADDI J,<1_<7-.RPCNT>>*LUNBLK
	SUBI J,<1_<6-.RPCNT>>*LUNBLK
]
	CAMGE J,Q
	 ADDI J,LUNBLK
	CAMGE J,Q
	 BUG
	MOVE D,UNFN1(J)
	TLC D,(SETZ)
	CAME A,D
	 JRST QLGL1
	MOVE D,UNFN2(J)
	TLC D,(SETZ)
	CAMLE B,D
QLGL2:	 ADDI J,LUNBLK
QLGL3:	TLC A,(SETZ)
	TLC B,(SETZ)
	POP P,E
	POP P,D
	JRST POPJ1

QLGL1:	CAML A,D
	 JRST QLGL2
	JRST QLGL3

;CALL BY JSP E,QLGLE
QLGLE:	MOVE D,UNFN2(J)
	TLC D,(SETZ)
	CAMN B,D
	 JRST QLGL3
	CAML B,D
	 JRST 1(E)
	JRST 2(E)

SUBTTL LOCKING ROUTINES

QMLOCK:	PUSHJ P,LSWTL
	    QMDRO
	POPJ P,

QMULK:	PUSH P,U
	MOVE U,USER
	MOVE U,LSWPR(U)
	HRRZ U,(U)
	CAIE U,QMDRO
	 BUG
	POP P,U
	JRST LSWPOP

QTLOCK:	PUSHJ P,LSWTL
	    QTUTO(I)
	POPJ P,

QTULK:	PUSH P,U
	MOVE U,USER
	MOVE U,LSWPR(U)
	HRRZ U,(U)
	CAIE U,QTUTO(I)
	 BUG
	POP P,U
	JRST LSWPOP

QUDLK:	PUSHJ P,LSWTL
	    QSNLCN(H)
	POPJ P,

QUDULK:	PUSH P,U
	MOVE U,USER
	MOVE U,LSWPR(U)
	HRRZ U,(U)
	CAIE U,QSNLCN(H)
	 BUG
	POP P,U
	JRST LSWPOP

SBTBLI:
QBTBLI:	440600,,	;IF GOING TO ILDB
SBTBL:
QBTBL:	360600,,
	300600,,
	220600,,
	140600,,
	060600,,
	000600,,

TBTBL=.+1
TBTBLI:	REPEAT 1+TUTEPW, TUTBP-<.RPCNT*TUTBYT*010000,,>

SUBTTL DIRECTORY GARBAGE COLLECTOR

; MOVE H, directory number
; lock the directory
; PUSHJ P,QGC
;  return if directory full.
;  return if won.
; No ACs clobbered.
; All pointers to directory relocated appropriately.

; Called from disk open routines if a new file is to be created
; and there are less than 6+LUNBLK words in the free area of the directory.
; Called from QSBWG if %QAFUL is set in QSRAC.
; %QAFUL means "must have successful GC before committing another track to this file."

;NOTE NOTE NOTE:
; This GC allocates an extra block of core for temporary storage.
; If no memory is free in low half, waits without unlocking directory.
; Can possibly PCLSR.  No deadly embrace to not unlock directory since
; it can't be flushed from core anyway and it's not holding anything else in.

QAPBMN==NXLBYT+3	;minimum number of descriptor bytes which
			; must be available at the end of a file to
			; commit another track to the file.
			;1 for a take-N, NXLBYT+1 for jump, 1 for ending zero.
QAPBMX==36.		;maximum number of descriptor bytes to allocate at a time.

QGC:	IRPC X,,ABCDQIJTR
	 PUSH P,X
	TERMIN

;Old GC prints message on system console.  Any reason to with fast one?

; validate the directory

QGC00:	SKIPL A,QSNLCN(H)
	 BUG			;directory not locked
	MOVE C,UDNAME(A)
	MOVEM C,LASTGC		;save name of last dir GCed
	CAME C,QSNUD(H)
	 BUG			;directory clobbered
	SKIPLE C,UDNAMP(A)
	CAILE C,2000
	 BUG			;name pointer out of bounds
	IMULI C,UFDBPW
	SKIPL D,UDESCP(A)
	CAIL D,-UDDESC*UFDBPW(C)
	 BUG			;descriptor/name overlap

; Get a block of core to use for temporary storage.
; The block is left in the "in-process" state.

	HRRZ Q,A
	PUSHJ P,TCALL
	    JRST IOMQ
	 JRST [	PUSHJ P,UDELAY	;no core available in low half
		JRST QGC00 ]	;so wait, then loop back to beginning
	LSH A,10.
	MOVEI B,1(A)		;zero it out
	HRL B,A
	SETZM (A)
	BLT B,1777(A)
	EXCH Q,A		;A -> directory, Q -> temporary core
;Drops through
;Drops in
; Scan the directory and count:
;  I	all files
;  J	files open for writing
;  R	active bytes of descriptor

	SETZB I,J
	SETZ R,
	MOVEI B,2000(A)		;end of name area
	ADD A,UDNAMP(A)		;start of name area
QGC10:	CAML A,B
	 JRST QGC19		;jump if all files done
	SKIPE UNFN2(A)
	SKIPN UNFN1(A)
	 BUG			;zero name block?
	AOJA I,QGC12		;file exists

QGC11:	ADDI A,LUNBLK		;advance to next file
	AOJA R,QGC10		;also count the zero byte that ends the descriptor

QGC12:	MOVE C,UNRNDM(A)
	TLNE C,UNWRIT
	 ADDI J,1
	ANDI C,.BM UNDSCP	;get descriptor ptr (undscp has pos=0)
	IDIVI C,UFDBPW
	HLL C,QBTBLI(D)
	ADDI C,UDDESC-2000(B)
	MOVE D,UNRNDM(A)
	TLNE D,UNLINK
	 JRST QGC14
QGC13:	ILDB T,C		;get byte of descrip
	JUMPE T,QGC11		;eof
	CAIG T,UDWPH
	 AOJA R,QGC13		;1-byte desc
REPEAT NXLBYT, IBP C		;multi-byte desc
	ADDI R,NXLBYT+1
	JRST QGC13

QGC14:	ILDB T,C		;count bytes of link descriptor
	JUMPE T,QGC11
	CAIN T,':
	 AOJA R,[IBP C		;quoted
		 AOJA R,QGC14 ]
	AOJA R,QGC14


; Compute number of bytes of descriptor to add after each
; file open for output.  If > QAPBMX, set to QAPBMX.
; If < QAPBMN, take directory-full exit.

QGC19:	MOVE C,I		;save number of files
	IMULI I,LUNBLK*UFDBPW	;number of bytes in name area
	ADDI R,UDDESC*UFDBPW(I)	;R := total number of bytes claimed
	MOVEI A,2000*UFDBPW-1
	SUB A,R			;A := number of bytes left
	SKIPE J
	 IDIV A,J		;number of bytes available to extend open files
	CAIGE A,QAPBMN
	 JRST QGC69		;if < minimum, dir. full
	CAILE A,QAPBMX
	 MOVEI A,QAPBMX		;limit to at most QAPBMX bytes at a time
;Drops through
;Drops in
; A = number of bytes of room to leave after each file open for writing
; B -> name block under consideration
; C -> old descriptors
; D = disk channel number under consideration.  Also D = C+1
; J counts new descriptor bytes generated
; Q -> new descriptors (in temporary core block)
; R -> base of directory

	SETZ J,
	HRRZ R,QSNLCN(H)	;old dir
	ADDI Q,UDDESC		;new dir
	HLL Q,QBTBLI
	MOVE B,UDNAMP(R)
	ADD B,R

; Loop over files

QGC20:	CAIL B,2000(R)
	 JRST QGC40		 ;all files done
	LDB C,[UNDSCP UNRNDM(B)] ;get old desc ptr
	DPB J,[UNDSCP UNRNDM(B)] ;store new desc ptr

; Loop over all disk channels, relocating the ones that point to this file

	MOVSI D,-NQCHN
QGC31:	HRRZ T,QUDPR(D)
	SKIPL QUSR(D)
	 CAIE T,(H)
	  AOBJN D,QGC31		;not in use or not same directory
	JUMPGE D,QGC39		;jump if all disk channels done
	LDB T,[1200,,B]
	CAME T,QUDFPR(D)
	 JRST QGC32		;not same file
	MOVE T,J		;relocate descriptor pointer
	SUB T,C
	ADDM T,QDIRP(D)
QGC32:	AOBJN D,QGC31

QGC39:	IDIVI C,UFDBPW		;make desc pntr into byte pointer
	HLL C,QBTBLI(D)
	ADDI C,UDDESC(R)
	MOVE T,UNRNDM(B)
	TLNE T,UNLINK
	 JRST QGC22
QGC21:	ILDB T,C		;copy descriptor bytes of a file into temporary core
	IDPB T,Q
	JUMPE T,QGC23
	CAIG T,UDWPH
	 AOJA J,QGC21
REPEAT NXLBYT,[
	ILDB T,C
	IDPB T,Q
]	ADDI J,NXLBYT+1
	JRST QGC21

QGC22:	ILDB T,C		;copy descriptor bytes of a link into temporary core
	IDPB T,Q
	JUMPE T,QGC23
	CAIN T,':
	 AOJA J,[ILDB T,C
		 IDPB T,Q
		 AOJA J,QGC22 ]
	AOJA J,QGC22

QGC23:	MOVE T,UNRNDM(B)
	TLNN T,UNWRIT
	 JRST QGC25
	ADD J,A			;file being written, increase desc area
	MOVE T,A
	IBP Q
	SOJG T,.-1

QGC25:	ADDI B,LUNBLK		;next file
	AOJA J,QGC20		;also account for final zero at end of descriptor

; Paranoia:  check that all disk channels to this directory look OK

QGC40:	MOVEM J,UDESCP(R)	;store free-desc pointer
	LDB A,[1200,,Q]		;last word used by descriptors
	CAML A,UDNAMP(R)
	 BUG			;overlap
	ANDI Q,-2000		;base of temp core block again
	MOVSI A,-NQCHN
QGC41:	HRRZ T,QUDPR(A)
	SKIPL QUSR(A)
	 CAIE T,(H)
	  AOBJN A,QGC41		;channel not in use, or to some other dir
	JUMPGE A,QGC49		;all channels done
	SKIPN B,QUDFPR(A)	;get file open on this channel
	 JRST QGC42		;channel not set up to any particular file
	CAIGE B,2000
	 CAMGE B,UDNAMP(R)
	  BUG			;file pointer screwed
	ADD B,R
	SKIPL J,QDIRP(A)	;get descriptor pointer
	 CAMLE J,UDESCP(R)
	  BUG			;descriptor pointer screwed
	LDB C,[UNDSCP UNRNDM(B)]
	CAIGE B,2000-LUNBLK(R)	;skip if last file in dir
	 LDB D,[UNDSCP UNRNDM+LUNBLK(B)] ;else get desc pntr for next file
	CAIL B,2000-LUNBLK(R)
	 MOVEI D,2000*UFDBPW	;but if last file, get infinity
	CAML J,C		;verify that QDIRP points to this file
	 CAML J,D
	  BUG
QGC42:	AOBJN A,QGC41		;do next channel

; If you thought that was paranoid, get a load of this:

QGC49:	MOVE B,UDNAMP(R)
	ADD B,R
QGC50:	CAIL B,2000(R)
	 JRST QGC60
	LDB C,[UNDSCP UNRNDM(B)]
	SOS C
	IDIVI C,UFDBPW		;NOTE if UNDSCP = 0 we depend on bytes
	HLL C,QBTBL(D)		; off left end of word LDB'ing as zero
	ADDI C,UDDESC(Q)
	LDB T,C
	SKIPE T
	 BUG			;descriptor not preceeded by zero
	MOVE J,UNRNDM(B)
	TRZ J,#.BM UNDSCP	;LH(J) flags, RH(J) just desc pntr
	TLNE J,UNLINK
	 JRST QGC52
QGC51:	ILDB T,C
	JUMPE T,QGC53
	CAIG T,UDWPH
	 AOJA J,QGC51
REPEAT NXLBYT, IBP C
	ADDI J,NXLBYT+1
	JRST QGC51

QGC52:	ILDB T,C
	JUMPE T,QGC53
	CAIE T,':
	 AOJA J,QGC52
	IBP C
	ADDI J,2
	JRST QGC52

QGC53:	TLNE J,UNWRIT		;RH(J) has desc pntr to the zero that ends the file
	 ADDI J,QAPBMN		;if being written, needs room for one more track
	CAIL B,2000-LUNBLK(R)	;now set D to point to next descriptor area
	 SKIPA D,UDESCP(R)	;do this instr if last file in dir
	  LDB D,[UNDSCP UNRNDM+LUNBLK(B)]	;else get next file's desc pntr
	CAIG D,(J)
	 BUG			;descriptors overlap
	ADDI B,LUNBLK	
	JRST QGC50

; Copy the descriptors back into the directory

QGC60:	MOVSI C,UDDESC(Q)	;make BLT pointer to copy it back
	HRRI C,UDDESC(R)
	MOVE B,UDNAMP(R)	;use same BLT to zero out the free area
	ADD B,R
	BLT C,-1(B)

; Take win return.

	AOS -9(P)
QGC69:	LDB A,[121000,,Q]	;core block number of temporary page
	PUSHJ P,TMEMR		;return it
	    MUINP		;verifying that it is the right one.
	IRPC X,,RTJIQDCBA
	 POP P,X
	TERMIN
	POPJ P,

SUBTTL INTERPRET DESCRIPTORS

QFNTR:	MOVE H,QUDPR(A)	;BYTE # IN Q CHNL # IN A
	PUSHJ P,QUDLK	;DONT SKIP IF OFF FILE
	MOVE T,QSNLCN(H) ;RETN BLOCK IN QSLGL(A) AND QSBI(A)  DIR PNTR IN QDIRP(A)
	ADD T,QUDFPR(A)	;FIRST ADR OF BLOCK IN QFBLNO(A)
	LDB TT,[UNDSCP+UNRNDM(T)]	;CHAR ADR OF FILE BEG
	MOVEM TT,QDIRP(A)	;SKIPS IF FINDS BLOCK AND LEAVES USER DIR LOCKED
	CLEARM QSBI(A)
	CLEARM QFBLNO(A)
	CLEARB J,QSLGL(A)	;J HAS ORG OF FIRST NON-EX-BLOCK IF OFF END OF FILE
QFNT1A:	PUSHJ P,QFNT1
	 JRST QUDULK	;OFF END OF FILE RETN NOT SKIPPING
	CAMLE J,Q
	 JRST QFNT7	;LOCATED BLOCK CONTAINING WORD
	MOVEM J,QFBLNO(A)
	JRST QFNT1A

QFNTN:	MOVE H,QUDPR(A)	;LIKE ABOVE BUT SCAN FORWARD ONLY FROM CURRENT SPOT
	PUSHJ P,QUDLK
	PUSHJ P,QFNT1
	 CAIA		;EOF, DON'T SKIP RETURN
	  AOS (P)
	MOVEM J,QFBLNO(A)
	JRST QUDULK

QFNT1:	SKIPE QSBI(A)	;DECODE NEXT BLOCK, CHNL IN A, SKIP UNLESS EOF.
	 JRST QFNT2	;BLOCK # IN QSLGL FILE ADR IN J (TAKE N PENDING)
QFNT3:	PUSHJ P,QMPDCH	;GOBBLE NEXT CHR OF DESC IN R (POINTED TO BY QDIRP) INCR QDIRP
	CAIN R,UDWPH	;ALSO RET BYTE PNTR IN TT
	 JRST QFNT3	;NULL
	JUMPN R,QFNT8	;NOT OFF END OF FILE
	SOS QDIRP(A)
	POPJ P,

QFNT8:	TRNE R,40
	 JRST QFNT4
	CAILE R,UDTKMX
	 JRST QFNT6	;SKIP AND TAKE
	MOVEM R,QSBI(A)
QFNT2:	SOS QSBI(A)
	AOS QSLGL(A)
QFNT5:	MOVEI J,2000	;ACTIVE BYTES IN BLOCK
	IMULI J,@QSBYTE(A)
	ADD J,QFBLNO(A)
	JRST POPJ1

QFNT7:	MOVSI T,%QALBK	;SET UP PROC LAST BLOCK FLG AND RETN
	ANDCAM T,QSRAC(A)	;CLEAR LAST BLOCK OF FILE BIT
	SKIPE QSBI(A)
	 JRST QFNT7A	;NOT LAST BLOCK
	ILDB R,TT	;GET NEXT CHR IN DIR
	JUMPN R,QFNT7A
	IORM T,QSRAC(A)	;PROCESSING LAST BLOCK
QFNT7A:	AOS QSBI(A)	;FAKE OUT PI ROUTINE WHICH WILL TRY TO INCREMENT
	SOS QSLGL(A)
	JRST POPJ1

QFNT4:	MOVEI J,0	;LOAD ADR
	DPB R,[140400,,J]
	PUSHJ P,QMPDCH
	DPB R,[060600,,J]
	PUSHJ P,QMPDCH
	DPB R,[0600,,J]
	MOVEM J,QSLGL(A)
	JRST QFNT5

QFNT6:	MOVEI J,1-UDTKMX(R)
	ADDM J,QSLGL(A)
	JRST QFNT5
EBLK

SUBTTL DISK CHANNEL DATA AREAS

QBFP:	BLOCK NQCHN	;BUFFER LIST LH LAST RH FIRST
			;READ: PI IN MP OUT.  WRITE: MP IN PI OUT.
SINLST:	BLOCK NQS	;SWAP-IN LISTS FOR EACH DISK
SOUTLS:	BLOCK NQS	;SWAP-OUT LISTS FOR EACH DISK

;READ/WRITE LISTS LINK THROUGH MLO IN MEMBLT.
;LH(MEMPNT) HAS DISK ADDR, RH(MEMPNT) HAS # BYTES IN BLOCK IF FILE READ
;MWC IN MEMBLT HAS WORD COUNT IF WRITE (FOR EXTRA-WORDS)

QFCHN:	NQCHN		;NUMBER OF FREE QSK CHNLS (NOT COUNTING DIR WRITE AND SWAP CHANNELS)

QUSR:	REPEAT NQCHN,-1	;USER
DWUSR:	-1	;0 IF DIR WRITE ACTIVE
SWUSR:	REPEAT NQS,-1	;0 IF SWAP XFER ACTIVE
QDIRP:	BLOCK NQCHN	;CHAR ADR PNTR TO DESC AREA FOR FILE
QSLBLK:	BLOCK NQCHN+NQS+1	;LAST BLOCK NUMBER OF FILE WRITTEN
QSLGL:	BLOCK NQCHN	;LAST QSGL
QSGL:	REPEAT NQCHN+NQS+1,-1	;-1 IDLE + ACTIVE TRACK
			;LAST NQS+1 FOR FD WRITE AND SWAPPING
QSBI:	BLOCK NQCHN	; COUNT CONSECUTIVE BLOCKS
QSBFS:	BLOCK NQCHN	;NUMBER OF BUFFS THIS CHNL +1 IF EOF AT PI WITH QSBFS=0
QPCLSR:	BLOCK NQCHN	;PCLSR STATUS OF MAIN PROGRAM - LOAD SYSTEM CALL
QSMDN:	BLOCK NQCHN	;MAIN PRGM ACTIVE BUFFER NUM
OLDRDT:	BLOCK NQCHN	;STORAGE FOR PREVIOUS REFERENCE DATE
QSRAC:	BLOCK NQCHN+1
	REPEAT NQS, %QMSWP ;SWAPPING CHANNELS START OUT IN SWAP MODE
 %QA==525252(1)
 %QALOK==400000		;4.9 CHNL LOCKED (NOT USED)
 %QAEFR==200000		;4.8 EOF REACHED READ
 %QAEFW==100000		;4.7 EOF WRITE
 %QACTH==40000		;4.6 DONT RELOAD. CORE JOB HUNG ON ACTIVE BUFFER
 %QAFUL==20000		;4.5 GC DIR BEFORE COMMITTING ANOTHER BLOCK TO FILE
 %QADEL==10000		;4.4 DELETE WHEN CLOSED
 %QAACC==4000		;4.3 FILE ADDRESS ALTERED BY .ACCESS OR OTHERWISE
 %QAPAR==2000		;4.2 NON RECOV PARITY ERR (OR OTHER DISK ERR) AT PI
 %QAOUT==1000		;4.1 ASSOC USER OUT DONT RELOAD (NOT IMPLEM.)
 %QALBK==400		;3.9 READ CHNL PROCESSING LAST BLOCK FLAG
 %QAMPU==200		;3.8 UPDATE QSMPRP, QSMPRC ON NEXT BUFFER GOBBLE
 %QAWOV==100		;3.7 FILLING OUT LAST BLK OF FILE IN WRITE OVER MODE PAST ORIG EOF
 %QALNK==40		;3.6 FILE IS REALLY A LINK
 %QALOP==20		;3.5 LINKS WERE TRACED IN OPENING THIS FILE.
 %QARWT==10		;3.4 MAKE WOULD-BE READERS WAIT (RATHER THAN GETTING FILE LOCKED ERROR)
 %QAFNY==4		;3.3 "FUNNY BLOCK", WORD COUNT IN LAST WORD
 $QAMOD==220200		;3.2-3.1 READ/WRITE MODE 0 NORMAL 1 WRITE OVER 2 COPY OVER WRITE
 %QAMWO==1		;3.1=1 => WRITE-OVER MODE.
 %QM==777777		;RH CHANNEL MODE.  INDEX INTO ACTION TABLES USED AT PI LEVEL.
.SEE %QMIDL		;FOR TABLE OF CHANNEL MODES

QDSKN:	BLOCK NQCHN	;DISK UNIT NUMBER
DWSKN:	0		;DISK WRITING DIR ON
SWSKN:	REPEAT NQS,.RPCNT	;DISK SWAP XFER
QSCRW:	BLOCK NQCHN	;0 READ -1 WRITE
	-1		;D.W.
	BLOCK NQS	;SWAP
QSCABN:	BLOCK NQCHN	;ACTIVE BUFFER NUMBER PI LEVEL OR -1 IF NONE
DWABN:	-1		;DISK D.W. A.B.N
SWABN:	REPEAT NQS,-1	;SWAP ABN
QUDPR:	BLOCK NQCHN	;NUMBER OF ASSOCIATED USER DIR PNTR
QUDFPR:	BLOCK NQCHN	;RELATIVE PNTR TO FILE AREA, ZERO IF NO PARTICULAR FILE
QMPTN:	BLOCK NQCHN	;HAS LAST TRACK STORED IN DIRECTORY
QMPTC:	BLOCK NQCHN	;MAIN PRGM TRACK COUNT
QMTTR:	BLOCK NQCHN	;TRACK RESERVED OR -1
QMFTP:	BLOCK NQCHN+1	;TRACK NUMBER FOR SCAN FOR FREE TRACKS IN TUT ON WRITE
	REPEAT NQS,NUDSL; DITTO FOR SWAP CHANNELS
QERRS:	BLOCK NQCHN+NQS+1 ;NUMBER ERRORS TRYING LAST OP
QSMPRP:	BLOCK NQCHN	;MAIN PRGM BYTE POINTER
QSMPRC:	BLOCK NQCHN	;M.P. COUNT (BYTES LEFT IN BUFFER)
QSBYTE:	BLOCK NQCHN	;LH BYTE PNTR (P=44), RH BYTES PER WORD
QSBSIZ==300600,,QSBYTE	;BYTE POINTER TO CHANNEL BYTE SIZE
QFBLNO:	BLOCK NQCHN	;BYTE # IN FILE OF BEG OF BLOCK BEING PROCESSED AT M.P. LEVEL
QRADAD:	BLOCK NQCHN	;DESIRED BYTE ADR (LOOKED AT IF %QAACC OR %QAMPU IN QSRAC SET)
QPIBSZ:	BLOCK NQCHN	;NUMBER OF BYTES IN BLOCK ACTIVE AT P.I. LEVEL (READ)
			;BIT 4.9 => GET FROM LAST WORD IN BLOCK (%QAFNY)
QMPBSZ:	BLOCK NQCHN	;NUMBER OF BYTES IN MN PROG ACTIVE BUFFER
QLDPTR:	BLOCK NQCHN	.SEE NLDSBQ ;DURING SBLK LOADING, HOLDS THE AOBJN POINTER
			;INTO USER CORE FOR DOING A DISK TRANSFER ON.
QSMMP:	BLOCK NQS	;MMP ADDRESS OF BLOCK ACTIVE ON SWAPPING CHANNEL

;VARIABLES FOR CURRENT TRANSFER

QSDU:	-1		;UNIT TRANSFERING DATA, -1 IF NONE.
QSDCH:	-1		;CHNL READY TO BE TRANSFERED ON QSDU
QERS1:	-1		;ERR VERIFY SWITCH -1 NO ERR 0 EXPECTING COMPLETION OF VERIFY OP
QDWIP:	0		;NUMBER OF DIR WRITE IN PROGRESS 4.9=1=>MASTER

IFN T300P,[
QSDU1:	-1		;QSDU FOR OTHER CONTROLLER
QSDCH1:	-1		;ASSOCIATED CHANNEL
QTUNT1:	-1		;UNIT TO TRANSFER NEXT
QTCHN1:	-1		;CHANNEL TO TRANSFER NEXT
NRXFR1:	0		;METERS
NWXFR1:	0
NSRXF1:	0
NSWXF1:	0
LQTM1:	0		;TIME LAST TRANSFER STARTED
];T300P

;MASTER FILE DIRECTORY

QMDRO:	-1		;ORIGIN OF MASTER DIR 4.9 IF LOCKED
			;-2 ON WAY IN 
			;4.6 NOT IN
			;4.5, 4.4, 4.3, ... CHANGED + NOT WRITTEN UNIT 0, 1, 2, ...
			;3.2=1 NOT RECONCILED (NQFUS NOT SET UP)
			;3.1-3.5 MUST BE ZERO (AT LEAST AFTER ITS RECONCILED)
QAMDNO:	-1		;ASCENDING MASTER DIRECTORY NUMBER, -1 NOT SET UP
NQFUS:	0		;NUMBER OF FREE LOSER SLOTS IN MFD

;TRACK UTILIZATION TABLES

QTUTO:	REPEAT NQS,-1	;ADDRESS OF TUT.  LH SIMILAR TO QMDRO
			;4.9 LOCK
			;4.8 READ-IN NOT STARTED YET
			;4.6 NOT READ IN YET
			;CHANGED AND NOT WRITTEN ON UNIT
			;0 = 4.5  1 = 4.4  2 = 4.3  3 = 4.2  4 = 4.1
			;5 = 3.9  6 = 3.8
			;3.1-3.5 MUST BE ZERO
QDPWF:	BLOCK NQS	;-1 TUT NOT RECONCILED (CALL QTCH1)
QTWRTM:	BLOCK NQS	;TIME TUT LAST WRITTEN
QSFT:	REPEAT NQS,-1	;NUMBER TRACKS FREE IN USER AREA
QSFTS:	BLOCK NQS	;NUMBER TRACKS FREE IN SWAPPING AREA
QPKNM:	BLOCK NQS	;NAME OF PACK
QPKID:	REPEAT NQS,-1	;PACK ID OF DISK ON DRIVE
IFN QRSRVP,[
QRESRV:	REPEAT NQS,-1	;NON-ZERO => PACK ON THIS DRIVE RESERVED.  SET FROM TUT.
			;NOT 0 AND NOT -1 => SIXBIT DEVICE NAME FOR "SECONDARY" PACK
];QRSRVP
NTBL:	 		;NUMBER OF BLOCKS IN TUT ON THIS DRIVE
IFE T300P, REPEAT NQS, NTUTBL
IFN T300P,[
	REPEAT T300P, NTUTBL
	REPEAT NQS-T300P, NTUTB1
];T300P

;POSITIONER VARIABLES (INDEXED BY PHYSICAL DRIVE)

QSKT1:	REPEAT NQS,-1	;CHANNEL POSITIONER SET FOR OR -1 IF NONE
QRCAL:	REPEAT NQS,0	;-1 IF RECALIBRATING
QSPPS:	REPEAT NQS,-1	;INTENDED POSITIONER POSITION (NOT USED???)
QSEEK:	BLOCK NQS	;-1 IF SEEKING
QPOS:	REPEAT NQS,-1	;CURRENT CYLINDER
QPOSGL:	BLOCK NQS	;CYLINDER TRYING TO POSITION TO
IFN DC10P,[
QRCTIM:	BLOCK NQS	;RECALIBRATE TIMEOUT IN HALF SECONDS.  THE ATTNS
];DC10P			; TEND TO GET LOST FOR SOME REASON

;MISCELLANEOUS DISK VARIABLES

QACT:	REPEAT NQS,0	;ONLY USE UNITS WITH 0
QACTB:	0		;4.5 UNIT 0 ACT  4.4 1 ACT  4.3 2  4.2 3  4.1 4
			;3.9 5  3.8 6
QWBUFS:	0		;TOTAL NUMBER OF WRITE BUFFERS ACTIVE

QWBFMX:	10.*DC10P+15.*RP10P+30.*RH10P+20.*RH11P  ;MAX # ALLOWED.  SHOULD BE
						 ; ABOUT 1 SEC DISK XFER.
QHUNGF:	0		;-1 => DISK CONTROL HUNG, PI LEVEL SHOULD RESET, RECALIBRATE, RETRY
NTQHNG:	BLOCK NQS	;NUMBER OF TIMES TRANSFER HUNG ON THIS UNIT

QTUNT:	0	;UNIT TO TRANSFER ON NEXT
QTCHN:	0	;CHNL ..
QLCHN:	0	.SEE QINT2C
QWRU:	-1	;DEFAULT WRITE UNIT (-1 if there isn't a default unit yet)
MDSK:	0	;DRIVE # OF MASTER DISK
QACTTM:	-1	;SETOM ON READ OR WRITE DATA TRANSFER
		;AOS AT SSLCK IF + OK TO WRITE DIRS
		;IE DON'T TIE UP DISK TO WRITE DIR UNLESS THINGS ARE QUIET
QDWFAR:	-10.	.SEE QINT3	;WRITE DIRS SOME OF THE TIME ANYWAY
LQTM:	0	;TIME AT WHICH LAST COMMAND GIVEN TO DISK
QFTTHR:	600.	;IF FREE TRKS ON QWRU GOES BELOW THIS, SWITCH TO UNIT WITH MOST FREE TRACKS
QRDAHD:	IFE MCOND DM,[0] .ELSE 2	;NUMBER OF BLOCKS TO READ AHEAD

LASTGC:	0	;NAME OF LAST DIRECTORY TO GET GC'ED

;USER DIRECTORIES

QNUD==40.		;NUMBER USER DIRECTORIES

QFUD:	QNUD		;NUMBER OF FREE UFD SLOTS

QSNUD:	BLOCK QNUD	;USER NAME OR 0 IF FREE
QSNLCN:	BLOCK QNUD	;4.9 LOCKED, 4.8 ALSO NOT IN CORE RH CORE ADR
	 .SEE DCHBT	;4.6 =1 NOT PAWED OVER 4.5 CHNGD AND NOT WRITTEN ON UNIT 0
			;4.4 UNIT 1 4.3 UNIT 2 4.2 UNIT 3 4.1 UNIT 4
			;3.9 UNIT 5 3.8 UNIT 6 3.7 UNIT 7
 %QUDWM==40		;3.6 WRITE IMMEDIATELY ON MASTER DISK
			;@ + XR BITS MUST = 0

QSNNR:	BLOCK QNUD	;NUMBER PC'S LOOKING AT THIS IE
			;+1 FOR FILE OPEN AND USER IN CORE(?)
QSNMI:	BLOCK QNUD	;TRACK N OF USER DIR IF NOT NEWLY CREATED
QSFBT:	BLOCK QNUD	;FREED-BLOCKS-TABLE.  HEAD OF A LIST OF PAGES,
			;THREADED THROUGH THE SECOND WORD AND ENDING WITH
			;ZERO.  FIRST WORD IS AOBJN POINTER TO FREE PART
			;OF PAGE.  FROM THIRD WORD UP TO BELOW WHERE AOBJN
			;POINTS ARE WORDS UNIT,,BLOCK WHICH RECORD DISK
			;BLOCKS TO BE FREED ONCE THE DIR HAS BEEN WRITTEN
			;OUT TO THE MASTER DISK.  ELIMINATES REUSED ADDRESS
			;PROBLEMS BY GUARANTEEING THAT IF THE SYSTEM SHOULD
			;CRASH AT ANY TIME, THERE CANNOT BE TWO DIRECTORIES
			;ON DISK THAT BOTH POINT TO THE SAME BLOCK.

IFN QRSRVP,[
QSALLO:	BLOCK QNUD	;-1 OR DRIVE # DIRECTORY ALLOCATED TO
]

;LOCKS

UDRSW:	-1	;USER DIR AREA LOCKED
	0
QCHSW:	-1	;CHNL AREA LOCKED
	0
QSKOSW:	-1	;QSKO1 LOCKED (USER DIR READIN)
	0

;LIST OF PAGES CONTAINING BLOCKS WHICH CAN NOW BE FREED.  MOVED TO
;HERE FROM QSFBT WHEN THE DIR IS WRITTEN OUT.  THE ACTUAL FREEING
;IS DONE BY THE CORE JOB.

QFBTS:	0

;VARIABLES FOR SYSTEMS CONCEPTS DISK CONTROL

IFN DC10P,[
QRCSW:	105	;- NO READ COMPARE 0 COMPARE WRITES + COMPARE ALL
QCST:	0	;CONI DC0, AT QINT
QERST:	0	;HAS CONI DC1, BITS WHEN AN ERROR HAPPENS
PKIDM:	0	;-1 WAITING FOR PACK ID TO BE READ
RPKID:	-1	;PACK ID READ INTO HERE BY DISK CONTROL
QDRSTS:	0	;DRIVE STATUS WORD STORED BY DISK CONTROL
]
IFE DMDSK,[
	0	;FOR BLT INTO QXWDS
QXWDS:	BLOCK NXWDS
]
IFN DC10P,[
QCHPRG:	0
QCHPR2:	DCOPY (-2000_2&37774)
	DCOPY (-NXWDS_2&37774)QXWDS
QCHPR4:	DHLT	;OR DRC
QCHPR3:	DCCOMP (-2000_2&37774)
	DCCOMP (-NXWDS_2&37774)QXWDS
	DHLT

GPKID:	DSPC+DSCRHD+DSWNUL+DUNENB+TUTCYL_11.+TUTSRF_6+TUTSEC
	DCOPY RPKID(37774)
	DHLT
QRECAL:	DSPC+DSRCAL+DUNENB
]

;VARIABLES FOR DEC RP10 DISK CONTROL

IFN RP10P,[
QERST:	0	;CONI DPC, ON ERROR
QERDTI:	0	;DATAI DPC, ON ERROR
QCST:	0	;CONI DPC, AT QINT
QCHPRG:	0	;SEEK COMM
IFN KA10P, QIOWD: -2000,,	;XFER DATA (HRRM ADR-1 IN RH)
IFE DMDSK,[
	-NXWDS,,QXWDS-1
	-200+NXWDS,,0
];DMDSK
	0

QRECAL:	700000,,
QGTBZY:	0	;FLAG FOR SOFTWARE AT INT
];RP10P
IFN QRDCMP,[
QRCSW:	0	;NON-ZERO TO ENABLE READ-COMPARING
RDCPHS:	0	;0 NORMAL, -1 DOING OPERATION WHICH SHOULD BE READ-COMPARED
		; AFTERWARD, + DOING READ-COMPARE
IFN KA10P,[
RCIOWD:	BLOCK 2	;CHANNEL PROGRAM FOR READ INTO QRCBUF
];KA10P		;ON KL WE HAVE TO PUT IT IN THE EPT!
QRCBUF:	BLOCK 2000
];QRDCMP

;VARIABLES FOR DEC RH10 DISK CONTROL

IFN RH10P,[
QCHPRG:	0	;DATA TRANSFER COMMAND
QCHPGA:	0	;ADDRESS (CYL IN LH, TRACK-SECTOR IN RH)
NCSHI:	0	;NUMBER OF CACHE INVALIDATES
NCSHIL:	0	;NUMBER OF LOOPS WAITING FOR CACHE INVALIDATES
NCSHU:	0	;NUMBER OF CACHE UNLOADS
NCSHUL:	0	;NUMBER OF LOOPS WAITING FOR CACHE UNLOADS
QERST:	0	;CONI AT LAST ERROR (LEAVE THIS THROUGH QECPAT IN SAME ORDER)
	0	;DATAI AT LAST ERROR
QERSTS:	0	;%HRSTS AT LAST ERROR
QERER1:	0	;%HRER1 AT LAST ERROR
QERER2:	0	;%HRER2 AT LAST ERROR
QERER3:	0	;%HRER3 AT LAST ERROR (IBM WOULD CALL THIS A CHANNEL LOGOUT AREA)
QERDCL:	0	;%HRDCL AT LAST ERROR
QECPOS:	0	;%HRPOS AT LAST ERROR
QECPAT:	0	;%HRPAT AT LAST ERROR
QCST:	0	;CONI AT LAST INTERRUPT
QGTBZY:	0	;FLAG FOR SOFTWARE-CAUSED INTERRUPT
QECCS:	BLOCK NQS	;NUMBER OF ECC CORRECTED ERRORS, PER DRIVE
QECCAD:	BLOCK NQS	;DISK ADDRESS OF MOST RECENT ECC CORRECTED ERROR
RHDATO:	-1		;LAST DATAO DSK, FOR DEBUGGING
ECCPAG:	BLOCK 8		;TEMP FOR SAVING PAGE MAP VARS AT QECC (WITH SPM)
NQDRE:	BLOCK NQS	;# MASSBUS TIMEOUTS PER DRIVE.
NQSATN:	BLOCK NQS	;# SPURIOUS ATTENTIONS IN MID-TRANSFER
NQEATN:	BLOCK NQS	;# ATTENTIONS REPORTING ERRORS
]

;VARIABLES FOR DEC RH11 DISK CONTROL (ON THE KS10 UNIBUS)

IFN RH11P,[
QCHPRG:	0	;DATA TRANSFER COMMAND
QCHPGA:	0	;ADDRESS (CYL IN LH, TRACK-SECTOR IN RH)
QIOWD:	0	; BA,,WC for RH11
QCST:	0	;CS1 AT LAST INTERRUPT
QGTBZY:	0	;FLAG FOR SOFTWARE-CAUSED INTERRUPT

QERST::		;Gubbish saved at last error
QERCS1:	0	; CS1	Control & Status 1
QERCS2:	0	; CS2	Control & Status 2
QERSTS:	0	; STS	Drive status
QERBA:	0	; BA	Unibus address for transfer
QERWC:	0	; WC	Word count for transfer
QERER1:	0	; ER1	Error 1
QERER2:	0	; ER2	Error 2
QERER3:	0	; ER3	Error 3
QERPOS::
QECPOS:	0	; POS	ECC position
QERPAT::
QECPAT:	0	; PAT	ECC pattern
QERMAP:	0 ? 0	; Unibus map to disk buffer

QECCS:	BLOCK NQS	;NUMBER OF ECC CORRECTED ERRORS, PER DRIVE
QECCAD:	BLOCK NQS	;DISK ADDRESS OF MOST RECENT ECC CORRECTED ERROR

NQSATN:	BLOCK NQS	;# SPURIOUS ATTENTIONS IN MID-TRANSFER
NQEATN:	BLOCK NQS	;# ATTENTIONS REPORTING ERRORS
NQOFFL:	BLOCK NQS	;# TIMES DISK WENT OFFLINE
]

DIRHNG:	0	;LH POINTS TO LIST OF DIRHNG DEVICE CHANNELS,
		;CHAINED THROUGH LH'S OF IOCHNM WORDS.
		;0 MEANS END OF LIST
		;MODIFY ONLY WITH THE CLOCK OFF.
	;EACH DIRHNG DEVICE CHANNEL'S IOCHST WORD RH CONTAINS THE
	;TRACK NUMBER OF THE DIRECTORY IT IS LOOKING AT.

BBLK

;DISK CONSTANTS

IFN RP10P,[
QATTNS:	REPEAT NQS,500000+.RPCNT_14,,1_<10-.RPCNT>
;WORDS TO CLEAR ATTNS AND SELECT DRIVES
]

SWAPL:	SINLST(Q)
	SOUTLS(Q)

DCHBT:	20000,,	;BIT SET IF DIR NOT WRITTEN OUT ON UNIT
	10000,,
	4000,,
	2000,,
	1000,,
	400,,
	200,,
	100,,
IFL .-DCHBT-NQS,.ERR YOU BETTER FIND ANOTHER BIT FOR QACTB,QSNLCN,QTUTO

IFN DC10P,[
QTRAN:	0	;FORMERLY MAPPED 203 CYL VIRTUAL DRIVES TO 406 CYL CALCOMPS
	1	;4.9 => 2ND HALF OF PHYSICAL DRIVE
	2	;(DOESN'T DO ANYTHING NOW THAT MEMOWRECKS ARE GONE,
	3	; BUT KEEP AROUND IN CASE EVER NEEDED AGAIN.)
	4
	5
	6
	7
IFL .-QTRAN-NQS,	.ERR BARF AT QTRAN
]

SUBTTL DISK CLOSE ROUTINES

QICL:	PUSH P,R
	SKIPGE QUSR(A)
	 BUG
	MOVSI Q,%QAACC
	ANDCAM Q,QSRAC(A)	;FLUSH RANDOM ACCESS HACKERY
	PUSHJ P,QICLW1		;WAIT FOR INPUT TO STOP
	MOVE TT,QSRAC(D)
	TLNE TT,%QADEL	;DELETE?
	 JRST QSICLD	;YES
	MOVE H,QUDPR(D)		    ;DIRECTORY NUMBER
	SETOM QUSR(D)
	AOS QFCHN
QICLX:	SOSGE QSNNR(H)		    ;FREE DIRECTORY CHANNEL HAD BEEN USING
	 BUG
	POP P,R
	SETZM (R)
	POPJ P,

QICLW1:	MOVE D,A		;IDLE CHANNEL AND FLUSH READ BUFFERS
	CONO PI,UTCOFF
	SKIPGE QSGL(D)
	 JRST QSICL3
	SKIPL QSCABN(D)
	 JRST QSICL4
	SETOM QSGL(D)

QSICL3:	HLLZS QSRAC(D) .SEE %QMIDL ;STOP PI
	CONO PI,UTCON
QSICL5:	PUSHJ P,QSBRB1	;RETURN M.P. BUFFER IF ANY
QOCL7:	HRRZ A,QBFP(D)	;ALSO ENTER TO RETURN UNUSED READ BUFFER ON WRITE OVER MODE
QSICL2:	JUMPE A,QSICL6
	LDB C,[MLO,,MEMBLT(A)]
	PUSHJ P,MEMR
	MOVE A,C
	SOS QSBFS(D)
	JRST QSICL2

QSICL6:	SETZM QBFP(D)
	POPJ P,

QSICL4:	MOVEI A,%QMRD1	;STOP PI AFTER THIS BLOCK
	HRRM A,QSRAC(D)
	CONO PI,UTCON
	SKIPL QSGL(D)	;WAIT FOR CHANNEL TO DEACTIVATE
	 PUSHJ P,UFLS
	JRST QSICL5

;OUTPUT CLOSE
QOCL:	PUSHJ P,QSOCL5	;CLEAN UP THE DISK CHANNEL
QSOCL4:	MOVE D,A	;ENTRY FROM QALINK
QSOCL6:	MOVE Q,QUDFPR(D)
	MOVE H,QUDPR(D)
	ADD Q,QSNLCN(H)
	MOVE TT,QSRAC(D)
	TLNE TT,%QADEL
	 JRST QCDLWO	;DELETED (PRESUMABLY VIA DELEWO)
	MOVE A,UNFN1(Q)	;Get names of file being hacked.
	MOVE B,UNFN2(Q)
	PUSHJ P,QLOOK	;FILING OVER ANYTHING?
	 JRST QSOCL3
	HRRZ I,QSNLCN(H)
	ADD I,QUDFPR(D)
	CAMN I,Q	;BEING WRITTEN BITS WENT AWAY?
	 BUG		;TRYING TO DELETE THE FILE WE'RE WRITING
	PUSHJ P,QSOCLD	;YES FLUSH IT
	PUSHJ P,QUDLK	;RELOCK DIR UNLOCKED BY QSOCLD ETC
	JRST QSOCL6	;MAKE SURE ONE HASN'T REAPPEARED WHILE DIR UNLOCKED

QSOCL5:	LDB H,[$QAMOD,,QSRAC(A)]
	JUMPN H,QOCL6	;DONT HACK ACTIVE WD COUNT IN WRITEOVER MODE
QOCL6A:	MOVN D,QSMPRC(A)	;- NUMBER OF BYTES IN BLOCK NOT USED
	ADDB D,QMPBSZ(A)	;ADJUST BYTE COUNT OF LAST BLOCK TO REFLECT WHATS USED
	CLEARM QSMPRC(A)	;SO WILL BE A NOOP IF PCLSR OUT AND COME BACK THRU HERE
QOCL4:	MOVSI Q,%QAEFW
	IORM Q,QSRAC(A)		;SET EOF BIT FOR QSBWW AND QUDS
	JUMPN H,QOCL5
	SKIPL QSMDN(A)		;NO M.P. BUFFER ANYWAY
	 JUMPE D,QOCL1		;ABOUT TO WRITE NULL BLOCK, DON'T
QOCL5:	PUSHJ P,QSBWW
QOCL2:	MOVE T,A
	PUSHJ P,[ SKIPGE QSCRW(T)	;WAIT FOR CHANNEL EITHER LEAVING WRITE MODE
		   SKIPN QSBFS(T)	;OR WRITING OUT ALL ITS BUFFERS, I.E. PI LEVEL
		    JRST POPJ1		;ISN'T GOING TO DO ANYTHING MORE TO IT.
		  POPJ P, ]
	 PUSHJ P,UFLS
	SKIPE QBFP(A)		;MAKE SURE IF QSBFS NON-ZERO THAT IS ONLY EOF
	 BUG			;NOT A BUFFER SITTING AROUND FORGOTTEN
QOCL3:	MOVE H,QUDPR(A)
	PUSHJ P,QUDLK
	SKIPN D,QMPTC(A)	;SKIP ON NEXT "N" STILL PENDING
	 POPJ P,
	PUSHJ P,QUDS		;STORE IT AWAY
	CLEARM QMPTC(A)
	POPJ P,

QOCL6:	SKIPGE QSCRW(A)	;WAIT FOR CHANNEL TO IDLE OR HANG UP IN READ
	 PUSHJ P,UFLS
	SKIPL QSGL(A)
	 PUSHJ P,UFLS
	MOVE D,A
	PUSHJ P,QOCL7
	MOVE A,D
	MOVE Q,QSRAC(A)
	MOVE B,QSBFS(A)
	JUMPE B,QOCL6B
	TLNE Q,%QAEFW
	 JRST QOCL6B
	TLNE Q,%QAEFR
	 SOS B,QSBFS(A)	;COMPENSATE FOR EXTRA AOS ON EOF READ
QOCL6B:	CAILE B,1
	 BUG
	TLNE Q,%QAWOV
	 JRST QOCL6A	;HACKING LAST BLOCK PAST EOF, UPDATE ACTIVE WD COUNT
	JRST QOCL4

QOCL1:	PUSHJ P,QSBRB	;DISCARD BUFFER
	SOS QSBFS(A)
	SOS QWBUFS
	MOVE D,QDSKN(A)
	AOS QSFT(D)	;RESTORE TRACK
	JRST QOCL2

QSOCL3:	MOVE Q,QUDFPR(D)
	MOVE TT,QSNLCN(H)
	ADDI Q,(TT)
	MOVSI T,UNWRIT
	ANDCAM T,UNRNDM(Q)	;CLEAR WRITE IN PROG
	MOVE TT,QSNLCN(H)
	MOVE T,UDNAMP(TT)	;MAKE SURE IT GOES BEFORE * FILE OF SAME NAME
	ADDI T,(TT)
	CAMN T,Q
	 JRST QSOC3E	;NO PREV FILE
	MOVE J,Q
QSOC3A:	SUBI J,LUNBLK
	CAMN A,UNFN1(J)
	CAME B,UNFN2(J)
	 JRST QSOC3B	;PREV FILE HAS DIFFERENT NAME
	CAILE J,(T)
	 JRST QSOC3A
QSOC3C:	CAMN J,Q
	 JRST QSOC3E
REPEAT LUNBLK,	PUSH P,.RPCNT(J)
	MOVE T,J
	HRL T,Q
	BLT T,LUNBLK-1(J)
REPEAT LUNBLK,	POP P,LUNBLK-.RPCNT-1(Q)
	SUB Q,J
	SUBI J,(TT)
	MOVSI T,-NQCHN
QSOC3D:	HRRZ A,QUDPR(T)
	SKIPL QUSR(T)
	 CAIE A,(H)
	  JRST QSOC3F
	HRRZ A,QUDFPR(T)
	CAIN A,(J)
	 ADD A,Q
	MOVEM A,QUDFPR(T)
QSOC3F:	AOBJN T,QSOC3D
QSOC3E:	SETOM QUSR(D)	;FREE THE DISK CHANNEL
	AOS QFCHN
	MOVE C,QSNUD(H)
IFN TPLP+UNSPLP,[
	MOVEI TT,SCRTPC
	CAMN C,[SIXBIT /.LPTR./]
	 IORM TT,SUPCOR	;CLOSING FILE ON TPL DEVICE SO SET FLAG FOR SYS JOB
];TPLP
	CAMN C,[SIXBIT/.MAIL./]
	 AOS NQMFWR	;CLOSING FILE ON .MAIL. => BUMP COUNT TO WAKE MAILER DEMON
IFN XGP,[
	CAMN C,[SIXBIT/.XGPR./]
	 AOS NXGPFW	;WAKE XGPSPL
];XGP
	MOVE A,QSNMI(H)	;TELL DIRHNG DEVICE ABOUT IT
	PUSHJ P,DIRSIG
	MOVE TT,QACTB	;DIR CHANGED (AT LEAST WRITE-IN-PROG BIT TURNED OFF)
	IORM TT,QSNLCN(H)	
	PUSHJ P,QUDULK
	SOSGE QSNNR(H)
	 BUG
	PUSHJ P,QSTRTR
	SETZM (R)	;CLEAR IOCHNM WORD.
	POPJ P,

QSOC3B:	ADDI J,LUNBLK
	JRST QSOC3C

QOCLR:	MOVE Q,QSRAC(A)	;CLOSE UNIT ASCII OUTPUT
	TLNN Q,%QAWOV
	 TLNN Q,%QAMWO+%QALNK
	  PUSHJ P,QOCLPD	;PUT NEEDED PADDING UNLESS NOT A FILE OR NOT AT END
	JRST QOCL

QOCLPD:	LDB Q,[360600,,QSMPRP(A)] ;NUMBER OF BITS NOT WRITTEN IN LAST WORD
	CAIL Q,44
	 POPJ P,	;LAST WORD NOT WRITTEN AT ALL
	LSH Q,18.+6	;FILL THOSE BITS WITH ^CS
	HRR Q,QSMPRP(A)
	MOVE C,[EOFWRD]
	SKIPLE QSMPRC(A) ;INHIBIT STORE IF NO BUFFER ETC.
	 DPB C,Q
	POPJ P,

SUBTTL DISK INTERRUPT ROUTINES

OVHMTR QIN	;DISK INTERRUPT LOW-LEVEL

IFN RP10P,[

QINT:	AOS QGTBZY
	MOVEM TT,QCST
IFN DMDSK,[
	TRNN TT,200000	;END OF CYLINDER
	 JRST QINT0
	SKIPGE C,QSDCH
	 BUG
	MOVE D,QSGL(C)
	IDIVI D,NBLKSC	;IS IT LEGITIMATE?
	SKIPL QSDU	;IF NO TRANSFER IN PROGRESS, IGNORE
	 CAIN E,NBLKSC-1	;IS LEGITIMATE FOR LAST BLOCK IN CYLINDER
	  CAIA		;IGNORE
	   BUG PAUSE,[MAYBE DISK CONTROL IS WRITING ALL OVER THE DISK AGAIN]
QINT0:	];DMDSK
	TDNE TT,[17177700]	;ANY ERRORS?
	 JRST QINTE
QINTN2:	DATAI DPC,R
	SKIPGE QSDU	;DONT CLOB CMD BUF TO CLR ATTS DURING DATA XFER
	 TRNN R,776	;ANY ATTNS
	  JRST QINTA	;NO
	LDB I,[11000,,R]	;YES,FIND WHICH DRIVE
	JFFO I,.+1	;CLOBBERS Q
	SUBI Q,28.

QINTAT:	DATAO DPC,QATTNS(Q)	;CLEAR ATTNS
	DATAI DPC,E
	TLNN E,40
	 JRST QRECAT		;NOT ON CYLINDER, RECALIBRATE
	LDB A,[DCYLI E]
	TRNE E,.BM DCYLXI
	 ADDI A,400
	CAME A,QPOSGL(Q)
	 JRST QRECAT	;SEEK TO THE WRONG PLACE (CALCOMPS LIKE TO DO THIS)
	MOVEM A,QPOS(Q)	;REMEMBER WHERE IT IS AT
	SETZM QSEEK(Q)
	SETZM QRCAL(Q)
	JRST QINT	;TRY AGAIN

QINTA:	SKIPGE Q,QSDU
	 JRST QINT1	;NOT EXPECTING DATA COMPLETION - FIND NEW TRANSFER
	CONSO DPC,400000
	CONSO DPC,10
	 JRST DSKEX	;TRANSFER STILL IN PROGRESS, DISMISS
	CONSZ DPC,20
	 JRST .-1	;BUSY?
	AOSN QHUNGF
	 JRST QHE	;XFER HUNG, RECALIBRATE AND RETRY
	JRST QINTA1	;TRANSFER COMPLETE...

QINTE:	DATAI DPC,R
	CONI DPC,TT	;THIS LOOKS REDUNDANT BUT APPARENTLY IS NECESSARY
			;DUE TO TIMING OF THE DISK-NOT-READY CONDITION
	CONO DPC,175700+DSKCHN	;RESET CONDITIONS
	MOVEM TT,QERST
	MOVEM R,QERDTI
	CONSZ DPC,20	;WAIT FOR UNBUSY
	 JRST .-1
	MOVE Q,QSDU	;FOR QOVR
	MOVE E,QCHPRG
	TRNE TT,20000	;OVERRUN
	 JRST QOVR
	TRNE TT,2000	;NOT READY
	 JRST QNRDY
	TDNE TT,[12010700]	;BITS 14,16,23,27-29 ARE LOSERS
	 BUG HALT,[DSK: TOTALLY FATAL ERROR, CONI=],OCT,QERST
	TLNE TT,5	;WD OR SECTOR PARITY ERROR
	 JRST QDE
	TRNE TT,1000	;WRITE LOCKED
	 JRST QIRWRE
	TRNE TT,40000	;SEARCH FAILED
	 JRST QHE
	TRNN TT,100000	;POWER FAILURE
	 JRST QINTE1
	CONSZ DPC,100000	;TRY IT AGAIN
	 JRST 4,.-1
	JRST QHE

QINTE1:	TLNE R,10	;FILE UNSAFE--NEED OPERATOR INTERVENTION
	 BUG PAUSE,[DSK: FILE UNSAFE UNIT ],DEC,Q
	TLNE R,4
	 BUG	;NO SUCH DRIVE
	TLNE R,110	;POSITION FAILURE (OR PROCEEDED FILE UNSAFE)
	 JRST QHE
	AOS NQSE(Q)	;SPURIOUS ERROR
	JRST QINTX	;IGNORE

QIRWRE:	BUG PAUSE,[DSK: WRITE LOCKED UNIT ],DEC,Q
	JRST QHE

QNRDY:	LDB Q,[410300,,R]	;WHICH DPC
	SKIPN QSEEK(Q)
	 SKIPGE QRCAL(Q)	;NOT SEEKING OR RECALIBRATING IS ERROR
	  JRST QINTN2
	BUG PAUSE,[DSK: UNIT ],DEC,Q,[NOT READY]
	JRST QREC
] ;END IFN RP10P

IFN DC10P,[

QINT:	MOVEM TT,QCST
	SKIPL PKIDM	;GETS SPURIOUS DIPE WHEN READING PACK ID
	 CONSO DC1,7777	;RUMOR THAT DSSERR DOESN'T ALWAYS SET
	  TRNE TT,DSSERR
	   JRST QINTE
	TRNE TT,DSSATT
	 JRST QINTAT
QINTA:	SKIPGE Q,QSDU
	 JRST QINT1	;NOT EXPECTING DATA COMPLETION, FIND NEW TRANSFER
	CONSZ DC0,DSSRUN+DSSACT
	 JRST DSKEX	;ACTIVE OR RUN, TRANSFER IN PROGRESS, DISMISS
	AOSN PKIDM	;SKIP IF NOT FINISHED READING PACK ID
	 JRST QSPKID
	AOSN QHUNGF
	 JRST QHE	;XFER HUNG, RECALIBRATE AND RETRY
	SKIPL QERS1
	 JRST QEROK	;OK ON VERIFY
	JRST QINTA1	;TRANSFER COMPLETE...

QINTE:	CONI DC1,TT
	CONO DC0,DCCLR+DCERR+DSKCHN	;CLEAR ERRORS
	MOVEM TT,QERST
	MOVE Q,QSDU
	CONSZ DC0,DSSRUN+DSSACT
	 JRST .-1	;ACTIVE OR RUN?
	SKIPL QERS1
	 JRST QERL1	;VERIFY ALSO LOST
	TRNE TT,DOFFL+DPROT+DDOBSY+DNXM+DCPERR
	 JRST QINTE1	;REALLY LOST BIG
	TRNE TT,DOVRRN
	 JRST QOVR
	TRNE TT,DWTHER+DFUNSF	;SEEK INC, END DISK, WATCHDOG, OR UNSAFE
	 JRST QHE	;TRY TO RECALIBRATE
	SKIPGE PKIDM
	 TRZ TT,DRLNER	;IGNORE LENGTH ERROR IF READING PACK #
	TRNE TT,DIPE+DRLNER+DRCER+DCKSER
	 JRST QDE	;TRY AGAIN
	SKIPGE PKIDM
	 TRNE TT,-1
	  AOS NQSE	;SPURIOUS ERROR (NOT LENGTH ERROR IN PKIDM)
	JRST QINTX	;SPURIOUS ERROR

QINTE1:	TRNE TT,DOFFL
	 BUG PAUSE,[DSK: UNIT ],DEC,Q,[OFFLINE]
	TRNE TT,DPROT
	 BUG PAUSE,[DSK: UNIT ],DEC,Q,[WRITE PROTECTED]
	TRNE TT,DDOBSY
	 BUG PAUSE,[DSK: DATAO WHEN BUSY]
	TRNE TT,DNXM+DCPERR
	 BUG PAUSE,[DSK: MEM PAR OR NXM ERROR]
	JRST QOVR

QINTAT:	CONI DC1,Q
	CONO DC0,DCCLR+DCCATT+DSKCHN	;CLEAR ATTENTION
	LDB Q,[DSATDN Q] ;ATTENTION DRIVE NUMBER
	CAIGE Q,NQS
	 SKIPL QRCAL(Q)
	  JRST QINTX
	SETZM QRCAL(Q)
	CONO DC0,DCCLR+DCATEB+DSKCHN	;CLEAR ATTENTION ENABLE
	JRST QINTX

;THIS IS CALLED EVERY HALF SECOND
QRCTMO:	MOVEI Q,NQS-1
	SKIPL QRCAL(Q)
QRCTM1:	 SOJGE Q,.-1
	JUMPL Q,CPOPJ
	SOSL QRCTIM(Q)
	 JRST QRCTM1
	BUG INFO,[DSK: RECAL TIMEOUT UNIT],DEC,Q
	SETZM QRCAL(Q)
	CONO DC0,DCCLR+DCATEB+DSKCHN	;CLEAR ATTENTION ENABLE I GUESS
	JRST QRCTM1
];DC10P

IFN RH11P,[

;;; RHCLRC(Q)	Clear controller errors and select drive Q
;;; RHSLCT(Q)	Select drive Q
;;; RHCMD(A)	Command in A to current drive
;;; RHCLRD	Clear current drive
;;; RHCHEK	Check for immediate bad news
;;;	All return CS1 in A

RHSLCT:	IOWRQ Q,%HRCS2
RHCHEK:	IORDQ A,%HRCS1
	TRNE A,%HXTRE+%HXMCP
	 BUG HALT,[DSK: MASSBUS ERROR, CS1=],OCT,A
	POPJ P,

RHCLRC:	IOWRQ Q,%HRCS2		; Must select drive before clearing controller
	MOVEI A,%HXTRE+%HXIE+%HMNOP
RHCMD:	TROA A,%HXIE
RHCLRD:	 MOVEI A,%HXIE+%HMCLR
	IOWRQ A,%HRCS1
	JRST RHCHEK

EBLK
DSKBRK:	0
BBLK
	JSR UTCSAV
QINT:	AOS QGTBZY
	MOVE Q,QSDU
	IORDQ TT,%HRCS1
	MOVEM TT,QCST
	TRNE TT,%HXTRE+%HXMCP
	 JRST QINTE
	TRNN TT,%HXSC		; Perhaps some drive needs attention?
	 JRST QINT0		; Not unless SC is set!
	IORDQ A,%HRATN
	JFFO A,QINTAT
QINT0:	SKIPGE Q,QSDU
	 JRST QINT1		; Not expecting completion of transfer.
	TRNN TT,%HXRDY
	 JRST DSKEX		; Transfer still in progress.
	AOSN QHUNGF
	 JRST QHE		; Transfer hung, recalibrate and retry.
	PUSHJ P,RHSLCT		; Select that drive.
	IORDQ A,%HRSTS		; Get its status.
	TRNE A,%HSERR		; Just in case %HXTRE didn't get set (by
	 JRST QINTE		;  analogy with RH10)...
	JRST QINTA1		; Transfer complete...

QINTAT:	MOVNI Q,-35.(B)
	HRRZS Q
	MOVEI A,1
	LSH A,(Q)
	IOWRQ A,%HRATN		; Turn off attention bit.
	CAIL Q,NQS
	 JRST QINT0
	CAMN Q,QSDU
	 JRST [	AOS NQSATN(Q)	; Ignore attention in mid-transfer.
		JRST QINTA2 ]
	SKIPE QACT(Q)
	 JRST QINTA2		; Ignore this drive.
	PUSHJ P,RHSLCT
	IORDQ B,%HRSTS		; Get status of drive with attention.
	TRC B,%HSMOL+%HSDPR+%HSRDY
	TRNN B,%HSMOL+%HSDPR+%HSRDY+%HSVV
	 PUSHJ P,QOFFL		; Went offline and came back online
	TRNE B,%HSERR+%HSPIP
	 JRST [	PUSHJ P,RHCLRD	; Drive barfing not during transfer
		AOS NQEATN(Q)	;  so clear it
		JRST .+1]	;THEN CLEAR SEEK AND RECALIBRATE FLAGS
	SETZM QSEEK(Q)		;POSITIONING COMPLETION
IFE RM03P,[			;No %HRCCY register on damn RM03s
	IORDQ A,%HRCCY
	MOVEM A,QPOS(Q)		;UPDATE CURRENT CYLINDER
	CAME A,QPOSGL(Q)
	 SETOM QSKT1(Q)		;SEEK TO WRONG PLACE
];RM03P
IFN RM03P,[
	MOVE A,QPOSGL(Q)	;On RM03, jump to conclusion
	MOVEM A,QPOS(Q)
];RM03P
	SETZM QRCAL(Q)		;NO LONGER RECALIBRATING
	JRST QINT0		;IF NO XFER ACTIVE, MAYBE CAN START ONE ON
				; THIS UNIT NOW

QINTA2:	SETOM QSKT1(Q)		;DON'T TRUST POS
	JRST DSKEX

; Went offline and came back online
QOFFL:	TRC B,%HSMOL+%HSDPR+%HSRDY	;Recover %HRSTS
	IORDQ C,%HRER1	
	IORDQ D,%HRER2
	IORDQ E,%HRER3
	BUG INFO,[DSK: UNIT #],DEC,Q,[CAME BACK ONLINE, CS1=],OCT,A,[STS=],OCT,B,[ER1=],OCT,C,[ER2=],OCT,D,[ER3=],OCT,E
	AOS NQOFFL(Q)
	MOVEI A,%HMCLR		;Clear the drive
	PUSHJ P,RHCMD
	MOVEI A,%HMRDP		;I said, clear the drive!
	PUSHJ P,RHCMD
	MOVEI A,0
	IOWRQ A,%HROFS		;No offset, 18 bits, ECC on, HCI off
	MOVEI A,%HMACK		;Turn %HSVV back on so drive will work
	PUSHJ P,RHCMD
	POPJ P,

QINTE:
IRPS X,,[CS1 CS2 BA WC POS PAT] ; First get status of controller
	IORDQ A,%HR!X
	MOVEM A,QER!X
TERMIN
	IORDQ A,UBAPAG+QUBPG_1	; Unibus map counts as part of
	MOVEM A,QERMAP		;  controller status.
	IORDQ A,UBAPAG+QUBPG_1+1
	MOVEM A,QERMAP+1
	SKIPGE Q		; Try hard to guess the drive.
	 LDB Q,[$HYDSK QERCS2]
	PUSHJ P,RHCLRC		; Clear controller errors and select drive
				; so that we can read its status too.
	TRNN A,%HXRDY		; Why would controller be busy?  (RH10
	 BUG			;  code checks for this...)
IRPS X,,[STS ER1 ER2 ER3]
	IORDQ A,%HR!X
	MOVEM A,QER!X
TERMIN
	MOVE A,QERCS1		; A: CS1
	MOVE R,QERCS2		; R: CS2
	SKIPN QERER2		; Bad news
	 SKIPE QERER3
	  JRST UNSAFE
	TRNN A,%HXMCP		; Real bad news
	 TRNE R,%HYWCE+%HYPE+%HYNED+%HYNEM+%HYPGE+%HYMXF
	  BUG HALT,[DSK: UNIT #],DEC,Q,[LOSING.  CS1=],OCT,A,[ CS2=],OCT,R
	TRNE R,%HYDLT+%HYMDP	; Data bus losing?
	 JRST QINTE1
	MOVE A,QERSTS		; Check Drive Status
	TRC A,%HSVV+%HSRDY+%HSDPR+%HSMOL	; Better be all on!
	TRCE A,%HSVV+%HSRDY+%HSDPR+%HSMOL
	 BUG
	TRNN A,%HSERR
	 JRST [	AOS NQSE(Q)	; Spurious?
		JRST QINT0]
	MOVE B,QERER1		; Check the main error register
	TRNE B,#<%H1ECC+%H1DTE+%H1CRC+%H1HCE+%H1ECH+%H1FER+%H1PAR>
	 JRST UNSAFE		; Those not listed above are considered "hard"
	CAIN B,%H1ECC		; Correct correctable error if that is the
	 JRST QECC		;  only problem.
	PUSHJ P,QINTER		; Else print message and reset drive
	TRNE B,%H1DTE+%H1CRC+%H1HCE+%H1FER	; These require recalibration
	 JRST QHE
	TRNE B,%H1ECC+%H1ECH+%H1PAR	; These require reread
	 JRST QDE
	JRST QOVR		; No error bits set?  (How can this
				; happen?)  Go and retry...

;;; Controller error:
QINTE1:	BUG INFO,[DSK: ERR UNIT #],DEC,Q,[CS1=],OCT,QERCS1,[CS2=],OCT,QERCS2
	JRST QOVR

;;; Drive error:
QINTER:	BUG INFO,[DSK: ERR UNIT #],DEC,Q,[ER1=],OCT,QERER1,[ER2=],OCT,QERER2,[ER3=],OCT,QERER3,[STARTING DISK ADDR=],OCT,QCHPGA
	JRST RHCLRD		; Reset error status in drive

;;; Come here for drive unsafe and similar bad things.  May be set to
;;; either halt or attempt retry (via USFHLT variable).  Note that when we
;;; get here the state of the drive has not yet been disturbed.
UNSAFE:	SKIPE USFHLT
	 BUG PAUSE,[DSK: TOO MANY ERRORS]
	MOVEI A,2		;HALT IF ANOTHER ERROR WITHIN 1/2 - 1 SECOND
	MOVEM A,USFHLT
	PUSHJ P,QINTER		;GIVE ERROR MESSAGE AND RESET DRIVE
	SKIPL QSDU
	 JRST QHE		;RECALIBRATE AND RETRY
	JRST QREC		;Just recalibrate, no transfer to retry

QECC:	PUSHJ P,RHCLRD		; Reset drive
	CAME Q,QSDU		; ECC error better be for transfer in
	 BUG			; progress...
	MOVE A,QCHPRG
	TRNN A,10	.SEE %HMRED
	 BUG			; ECC error should only happen during read.
	LDB A,[$UPPAG QERMAP]
	IORI A,600000
	DPB A,[.PAREP+EXEUMP]	; Point parity error page at buffer.
	CLRPT 400000+PAREP_12	; Get it into our map now.
	CLRPT 401000+PAREP_12	; Both halves...
	MOVE J,QERBA
	SUBI J,1000+QUBPG_14	; Correct to relative byte address of start
				; of losing sector.
	TRNE J,770777		; Should always be aligned on sector boundary
	 BUG			;  and within a single block, right?
	LSH J,-2		; J: Address of first word of losing sector
				;  within block.
	MOVE A,QECPOS		; Get error position
	SOJL A,QDE		;HARDWARE POS IS OFF BY 1; IF 0 NOT CORRECTABLE
	IDIVI A,36.		;CONVERT TO WORD NO AND BIT NO
	CAIL A,177
	 JRST [	ADDI B,36.	;LAST WORD IN SECTOR - HACK IT TO AVOID NXM
		SOJA A,.+1]
	ADDI J,400000+PAREP_12(A)	; J: Address of losing word-pair
					; (In parity error page.)
	MOVE U,B		;SAVE BIT NUMBER
	MOVS B,(J)		;GET FIRST LOSING WORD
	MOVS C,1(J)		;GET SECOND LOSING WORD
	MOVE D,QECPAT		;GET ERROR PATTERN
	SETZ E,			;MAKE INTO DOUBLE-WORD
	ROTC D,(U)		;ALIGN IT
	XOR B,D			;FIX THE BAD BITS
	XOR C,E
	MOVSM B,(J)		;PUT CORRECTED DATA BACK
	MOVSM C,1(J)
	MOVEI A,0
	DPB A,[.PAREP+EXEUMP]	;FLUSH THE DISK BUFF FROM MAP
	CLRPT 400000+PAREP_12
	CLRPT 401000+PAREP_12	; Both halves...

	AOS QECCS(Q)		;LOG THE LOSS
	LDB E,[111000,,QERBA]	; E: Number of sectors transferred (including
	SUBI E,QUBPG_3		;  ECC sector)
	MOVE A,QCHPGA
	LDB B,[$HASEC A]	;Compute disk address of failing sector:
	ADDI B,-1(E)
	IDIVI B,NSECS		; C: sector
	DPB C,[$HASEC A]
	LDB D,[$HATRK A]
	ADD B,D			; B: track
	DPB B,[$HATRK A]
	MOVEM A,QECCAD(Q)
	HLRZ A,A		; A: cylinder
	MOVE D,QSDCH		; D: chnl
	BUG INFO,[DSK: ECC CORRECTED ERROR, UNIT #],DEC,Q,[CYL ],DEC,A,[HEAD ],DEC,B,[SEC ],DEC,C,[QSRAC],OCT,QSRAC(D)
	CAIL E,SECBLK
	 JRST QINTA1		;XFER COMPLETE

	LDB B,[$HASEC QCHPGA]	;NOW DETERMINE WHERE TO RESUME TRANSFER
	ADD B,E
	IDIVI B,NSECS
	DPB C,[$HASEC QCHPGA]
	LDB C,[$HATRK QCHPGA]
	ADD C,B
	DPB C,[$HATRK QCHPGA]	;NO NEED TO IDIVI A,NHEDS SINCE NEVER CROSS
				; CYLINDERS

	HRL E,QERWC		; Reassemble "iowd" from
	HRR E,QERBA		; saved parts.  (Don't trust stuff saved
	MOVEM E,QIOWD		; in controller more than we have to.)
	HRRZ C,QSDCH		;FOR QECCX
	JRST QECCX		;MORE TO DO, CONTINUE XFER

] ;RH11P

IFN RH10P,[

QINT:	AOS QGTBZY
	MOVE Q,QSDU
	MOVEM TT,QCST
	TRNE TT,%HIERR
	 JRST QINTE
	TRNN TT,%HIATN
	 JRST QINT0
	MOVSI A,%HRATN		;FIND DRIVES NEEDING ATTENTION
	PUSHJ P,RHGET
	JFFO A,QINTAT
QINT0:	SKIPGE Q,QSDU
	 JRST QINT1		;NOT EXPECTING COMPLETION OF TRANSFER
	TRNN TT,%HIDON
	 JRST DSKEX		;NO COMPLETED TRANSFER
	CONSZ DSK,%HIBSY
	 JRST 4,.-1		;TRANSFER STILL IN PROGRESS??
	AOSN QHUNGF
	 JRST QHE		;XFER HUNG, RECALIBRATE AND RETRY
	MOVSI A,%HRSTS(Q)	;SOMETIMES %HSERR SETS AND %HIERR DOESN'T!
	PUSHJ P,RHGET
	TRNE A,%HSERR
	 JRST QINTE
	JRST QINTA1		;XFER COMPLETE...

QINTAT:	MOVNI Q,-35.(B)		;LOW BIT IS DRIVE 0
	HRRZS Q
	MOVEI A,1		;TURN OFF ATTENTION (DRIVE MAY BE LOSING
	LSH A,(Q)		;IN SUCH A WAY THAT READING %HRSTS DOESN'T CLEAR ATTN)
	HRLI A,%HRATN
	PUSHJ P,RHSET
	CAMN Q,QSDU
	 JRST [	AOS NQSATN(Q)	;IGNORE ATTN IN MID-TRANSFER
		JRST QINTA2 ]
	MOVSI A,%HRSTS(Q)	;GET STATUS OF DRIVE WITH ATTENTION
	PUSHJ P,RHGET		;THIS ALSO CLEARS THE ATTENTION BIT
	SKIPE QACT(Q)
	 JRST QINTA2		;IGNORE THIS DRIVE
	TRNE A,%HSERR+%HSPIP
	 JRST [	MOVSI A,%HRDCL(Q)
		HRRI A,%HMCLR	;DRIVE REPORTING ERROR NOT DURING TRANSFER
		PUSHJ P,RHSET	;SO CLEAR THE DRIVE
		AOS NQEATN(Q)
		JRST .+1 ]	;THEN CLEAR SEEK AND RECALIBRATE FLAGS
	SETZM QSEEK(Q)		;POSITIONING COMPLETION
	MOVSI A,%HRCCY(Q)
	PUSHJ P,RHGET
	MOVEM A,QPOS(Q)		;UPDATE CURRENT CYLINDER
	CAME A,QPOSGL(Q)
	 SETOM QSKT1(Q)		;SEEK TO WRONG PLACE
	SETZM QRCAL(Q)		;NO LONGER RECALIBRATING
	JRST QINT0		;IF NO XFER ACTIVE, MAYBE CAN START ONE ON THIS UNIT NOW

QINTA2:	SETOM QSKT1(Q)		;DON'T TRUST POS
	JRST DSKEX

;INPUT FROM MASSBUS -  ADDRESS IN LH(A), RETURNS DATA IN A

RHGET:	TLZA A,%HRLOD

;OUTPUT TO MASSBUS -  ADDRESS IN LH(A), DATA IN RH(A)

RHSET:	 TLO A,%HRLOD
	DATAO DSK,A
	MOVEM A,RHDATO		;SAVE FOR REBUGGING
	MOVEI A,5		;WAIT 3 USEC FOR BUS CYCLE
	SOJG A,.
	DATAI DSK,A
	TLNE A,%HDERR
	 BUG HALT,[DSK: MASSBUS ERROR, DATAO=],OCT,RHDATO,[DATAI=],OCT,A
	ANDI A,177777		;RETURN 16-BIT REGISTER CONTENTS
	POPJ P,

QINTE:	CONI DSK,TT
	DATAI DSK,R
	CONO DSK,%HOCLR+%HORAE+%HOATN+DSKCHN	;RESET CONTROLLER SO DRIVE STATUS MAY BE READ
	CONSZ DSK,%HIBSY
	 JRST .-1		;SOMETIMES CONTROLLER IS BUSY??
	MOVEM TT,QERST
	MOVEM R,QERST+1
	SKIPGE Q		;TRY HARD TO GUESS THE RIGHT DRIVE!
	 LDB Q,[$HCDRV R]	;CURRENTLY SELECTED DRIVE
	IRPS X,,[%HRSTS %HRER1 %HRER2 %HRER3 %HRDCL %HRPOS %HRPAT]
	 MOVSI A,X(Q)		;GET STATUS OF DRIVE
	 PUSHJ P,RHGET
	 MOVEM A,QERST+.IRPCNT+2
	TERMIN
	SKIPN QERER2		;FILE UNSAFE
	 SKIPE QERER3
	  JRST UNSAFE
	TRZE TT,%HIDRE
	 AOS NQDRE(Q)		;THIS BIT SUSPECTED OF BEING SET RANDOMLY.
	;;CHECK FOR IRRECOV (?) HARDWARE LOSSAGE. EVEN MORE SO THAN 'UNSAFE'
	TDNE TT,[%HIILF+%HISDE+%HINXM+%HIDRE+%HIILC+%HIPWR]	
	 BUG HALT,[DSK: UNIT ],DEC,Q,[ LOSING. RH10 CONI= ],OCT,QERST
IFN KS10P, .ERR So whats this all about?
IFN KL10P,[
	TLNN TT,(%HIDPE+%HICPE)	;IF CHANNEL DETECTS PARITY ERROR, CAUSE
	 JRST QINTE0		;PROCESSOR PARITY SWEEP WHICH WILL PROBABLY
	MOVSI A,SCLPAR		;CRASH THE SYSTEM ANYWAY.  BEATS LOOPING!
	TDNN A,SUPCOR		;BUT DON'T CAUSE CLKB1E+7 HALT
	 CONO 10407
	BUG CHECK,[DSK: MEM PAR ERR, QICWA/],OCT,QICWA,OCT,QICWA+1,[QIOWD/],OCT,QIOWD
QINTE0:
];KL10P
	TDNE TT,[%HIOVR+%HICOV+%HIDPE+%HICPE+%HIBPE]
	 JRST QINTE1		;OVERRUN OR PARITY ON CONTROL BUS OR CHANNEL BUS - RETRY
	MOVE A,QERSTS		;DRIVE EXCEPTION, CHECK DRIVE STATUS
	TRC A,%HSVV+%HSRDY+%HSDPR+%HSMOL	;THESE BITS BETTER ALL BE ON
	TRCE A,%HSVV+%HSRDY+%HSDPR+%HSMOL
	 BUG			;DRIVE TURNED OFF?  MAYBE 11 HACKING THIS DRIVE?
	TRNN A,%HSERR
	 JRST [	AOS NQSE(Q)	;SPURIOUS?
		JRST QINT0 ]
	MOVE B,QERER1		;CHECK THE MAIN ERROR REGISTER
	TRNE B,#%H1SOF		;ANY HARD ERRORS?
	 JRST UNSAFE
	CAIN B,%H1ECC		;IF JUST A CORRECTABLE ERROR,
	 JRST QECC		;GO CORRECT IT
	PUSHJ P,QINTER		;OTHER SOFT ERROR, PRINT MESSAGE AND RESET DRIVE
	TRNE B,010620		;SEARCH ERROR, OR HEADER READ OR FORMAT ERROR OR DRIVE TIMING 
	 JRST QHE		;REQUIRES RECALIBRATE
	TRNE B,100110		;BUS PARITY OR ECC "HARD"
	 JRST QDE		;REQUIRES RE-READ, GIVE UP AFTER N
	JRST QOVR		;NO ERROR BITS, PRINT MESSAGE AND RETRY

;CONTROLLER ERROR
QINTE1:	BUG INFO,[DSK: ERR UNIT #],DEC,Q,[CONI=],OCT,QERST,[DCL=],OCT,QERDCL
	JRST QOVR

;DRIVE ERROR
QINTER:	BUG INFO,[DSK: ERR UNIT #],DEC,Q,[ER1=],OCT,QERER1,[ER2=],OCT,QERER2,[ER3=],OCT,QERER3,[STARTING DISK ADDR=],OCT,QCHPGA
	MOVSI A,%HRDCL(Q)	;NOW RESET ERROR STATUS IN DRIVE
	HRRI A,%HMCLR
	JRST RHSET

;COME HERE FOR DRIVE UNSAFE AND SIMILAR BAD THINGS.  MAY BE SET TO
;EITHER HALT OR ATTEMPT RETRY (VIA USFHLT VARIABLE).  NOTE THAT WHEN
;WE GET HERE THE STATE OF THE DRIVE HAS NOT YET BEEN DISTURBED.
UNSAFE:	SKIPE USFHLT
	 BUG PAUSE,[DSK: TOO MANY ERRORS]
	MOVEI A,2		;HALT IF ANOTHER ERROR WITHIN 1/2 - 1 SECOND
	MOVEM A,USFHLT
	PUSHJ P,QINTER		;GIVE ERROR MESSAGE AND RESET DRIVE
	JRST QHE		;RECALIBRATE AND RETRY

QECC:	MOVSI A,%HRDCL(Q)	;RESET ERROR STATUS IN DRIVE
	HRRI A,%HMCLR
	PUSHJ P,RHSET
	CAME Q,QSDU		;CORRECT AN ECC ERROR
	 BUG			;BARF ... NO TRANSFER IN PROGRESS
	MOVE A,QCHPRG
	TRNN A,10
	 BUG			;BARF ... SHOULDN'T HAPPEN DURING WRITE
	SKIPN A,QICWA+1		;GET ADDRESS OF LAST WORD READ
	 JRST 4,.-1		;CHANNEL OUGHT TO HAVE STORED CONTROL WORD BY NOW
	SOS J,A
	TDZ J,[177+.BM $DFCWA]	;J := ADDRESS OF FIRST WORD IN LOSING SECTOR
	LDB A,[$DFCAD QIOWD]
	SUBM J,A
	SOS H,A			;# WORDS SUCCESSFULLY XFERED.
	CAIL H,0		;CHECK IF CHANNEL STORED BAD ADDRESS
	 CAILE H,2000-200
	  JRST QDE		;THIS SHOULD NEVER HAPPEN, BUT IT DOES, FREQUENTLY
	MOVE A,QECPOS		;GET ERROR POSITION
	SOJL A,QDE		;HARDWARE POS IS OFF BY 1; IF 0 NOT CORRECTABLE
	IDIVI A,36.		;CONVERT TO WORD NO AND BIT NO
	CAIL A,177
	 JRST [	ADDI B,36.	;LAST WORD IN SECTOR - HACK IT TO AVOID NXM
		SOJA A,.+1]
	ADD J,A			;J := ADDRESS OF LOSING WORD-PAIR
	LDB A,[121400,,J]
	ANDI J,1777		;GET ADDR OF PAR ERR PAG
	ADDI J,400000+2000*PAREP ;IN EXEC ADDRESS SPACE
	IORI A,600000		;MAKE THAT PAGE POINT TO THE DISK BUFFER
	DPB A,[.PAREP+EXEUMP]
	SPM ECCPAG		;GET IT INTO OUR MAP.
	LPMR ECCPAG
	MOVE U,B		;SAVE BIT NUMBER
	MOVS B,(J)		;GET FIRST LOSING WORD
	MOVS C,1(J)		;GET SECOND LOSING WORD
	MOVE D,QECPAT		;GET ERROR PATTERN
	SETZ E,			;MAKE INTO DOUBLE-WORD
	ROTC D,(U)		;ALIGN IT
	XOR B,D			;FIX THE BAD BITS
	XOR C,E
	MOVSM B,(J)		;PUT CORRECTED DATA BACK
	MOVSM C,1(J)
	MOVEI A,0
	DPB A,[.PAREP+EXEUMP]	;FLUSH THE DISK BUFF FROM MAP
	LPMR ECCPAG

	AOS QECCS(Q)		;LOG THE LOSS
	MOVE A,QCHPGA
	MOVEI E,200(H)
	LSH E,-7		;NUMBER OF SECTORS TRANSFERRED (INCLUDING ECC SECTOR)
	LDB B,[$HASEC A]	;GET DISK ADDR OF FAILING SECTOR
	ADDI B,-1(E)
	IDIVI B,NSECS
	DPB C,[$HASEC A]
	LDB D,[$HATRK A]
	ADD B,D
	DPB B,[$HATRK A]	;NO NEED TO DIVIDE BY NHEDS SINCE NEVER CROSS CYLINDERS
	MOVEM A,QECCAD(Q)
	HLRZ A,A
	MOVE D,QSDCH
	BUG INFO,[DSK: ECC CORRECTED ERROR, UNIT ],DEC,Q,[CYL ],DEC,A,[HEAD ],DEC,B,[SEC ],DEC,C,[QSRAC],OCT,QSRAC(D)
	LDB B,[$HASEC QCHPGA]	;NOW DETERMINE WHERE TO RESUME TRANSFER
	ADD B,E
	IDIVI B,NSECS
	DPB C,[$HASEC QCHPGA]
	LDB C,[$HATRK QCHPGA]
	ADD C,B
	DPB C,[$HATRK QCHPGA]	;NO NEED TO IDIVI A,NHEDS SINCE NEVER CROSS CYLINDERS
	HRRZ C,QSDCH		;FOR QECCX
	LDB A,[$DFCWA QICWA+1]	;LAST CONTROL WORD PROCESSED
	SUBI A,1		;CRETINOUS DF10
	CAIE A,QIOWD
	 BUG			;CHANNEL STORED BAD ADDRESS?
	HRRZM A,QICWA
	MOVEI A,200(H)		;# WORDS ALREADY DONE.
	DPB A,[$DFWC A]		;PUT IT IN BOTH FIELDS.
	ADDB A,@QICWA		;ADVANCE C.W. TO REMAINING STUFF
	TLNE A,(.BM $DFWC)
	 JRST QECCX		;MORE TO DO, CONTINUE XFER
	AOS QICWA		;ELSE ADVANCE TO NEXT C.W.
	SKIPE @QICWA
	 JRST QECCX		;MORE TO DO, CONTINUE XFER
	JRST QINTA1		;XFER COMPLETE
]

IFN T300P,[
T3INT:	SETZM DSCDON		;CLEAR DONE FLAG THAT GOT US HERE
	SKIPGE QSDU1		;OPERATION COMPLETE?
	 JRST QINT1		;NO, GO FIND SOMETHING TO DO
	HRRZ Q,DSCDRV		;YES, GET DRIVE OPERATION WAS ON
	ADDI Q,T300P
	CAIGE Q,NQS
	 CAME Q,QSDU1		;TERMINATION ON DRIVE WE EXPECTED?
	  BUG			;DRIVE NUMBER CLOBBERED?
	SETZM QRCAL(Q)		;PRESUMABLY NOT RECALIBRATING ANY MORE.
	MOVE A,DSCCMD		;COMMAND THAT JUST COMPLETED
	CAIN A,%DMREC		;WAS IT A RECALIBRATE?
	 JRST QINT1E		;IF SO, CONTROLLER IS IDLE NOW
	MOVE C,QSDCH1		;CHANNEL
	HRLZ E,DSCFLT		;CHECK FOR ERRORS
	HRR E,DSCSTS
	JUMPE E,QINTI		;OPERATION COMPLETED UNEVENTFULLY
	MOVE D,DSCHED		;GET DISK ADDRESS AS CYL,,HEAD_9+SECTOR
	LSH D,9
	IOR D,DSCSEC
	HRL D,DSCCYL
	SKIPN QERRS(C)		;PRINT ONLY ONCE, NOT ON RETRIES
	 BUG INFO,[DSK: T-300 ERR UNIT ],DEC,Q,[FAULT=],OCT,DSCFLT,[STATUS=],OCT,DSCSTS,[CMD=],OCT,DSCCMD,[CYL-SURF-SEC=],OCT,D
	TRZE E,%DSRTR+%DSECC	;THESE ARE NOT ERRORS	
	 AOS QECCS(Q)
	JUMPE E,QINTI		;OPERATION COMPLETED SUCCESSFULLY (WITH INTERVENTION OF 2561)
	MOVE T,E
	TRZ T,%DSECH+%DSIDE+%DSHCE
	JUMPN T,T3INT2		;CONSIDER USING ERROR RECOVERY FEATURES
	TRNE A,%DMRED		;IF COMMAND IS A READ
	 TRNE A,10		;AND WE HAVEN'T TRIED THEM ALL
	  JRST T3INT2
	AOS NQSE(Q)		;COUNT "SOFT ERRORS" (NOT "SPURIOUS" IN T-300 CASE)
	AOJA A,T3IO1		;GO RETRY OPERATION USING NEXT FEATURE

T3INT2:	TLNN E,-1		;ANY FAULT?  PROBABLY UNRECOVERABLE BUT TRY RECALIBRATE
	 TRNE E,%DSIDE+%DSHCE+%DSSKE+%DSOFL+%DSFLT	;DISK CONDITION THAT CALLS FOR RECAL
	  JRST T3HE
	JRST T3DE		;TRY OPERATION AGAIN.  PROBABLY WILL LOSE BUT TRY.
];T300P

OVHMTR QSC	;DISK SCHEDULER (AND HIGHER-LEVEL INTERRUPT STUFF)

QINTA1:	SKIPL C,QSDCH		;XFER COMPLETE
QINTI:	SKIPGE A,QSCABN(C)	;CHANNEL IN C
	 BUG			;MEMBLT INDEX IN A
	MOVE R,A		;BUFFER ADDRESS IN R
IFN KL10P,[			;QSRAC IN D, QSK# IN Q
	SKIPL QSCRW(C)		;IF READ, FLUSH CACHE AGAIN
	 JRST [	PUSHJ P,CSHSWP	;SINCE USER MIGHT HAVE TOUCHED ABS PAGE
		    CAIA
		MOVE A,R	;A WAS CLOBBERED
		JRST .+1 ]
]
IFN KS10P,[
	SKIPL QSCRW(C)		;ON KS, CAN ONLY CLEAR THE WHOLE CACHE
	 CLRCSH
]
	LSH R,10.
	MOVE D,QSRAC(C)
IFN RP10P,[
	HRRZ TT,QICWA+1
	CAIE TT,1777(R)
	 TLNE D,%QAPAR		;IGNORE IF LOSING ANYWAY (E.G. SEARCH ERR)
	  CAIA
	   JRST QDFLOS
];RP10P
IFN QRDCMP,[
	SKIPE TT,RDCPHS		;NEED TO READ-COMPARE?
	 JRST QRC1		;READ-COMPARE STARTING OR FINISHED
QRC0:
];QRDCMP
	TRNE D,-%QMMAX
	 BUG
	XCT .+1(D)		;INVOKE COMPLETION HANDLER
	OFFSET -.
%QMIDL::JRST 4,QINT1		;IDLE - SHOULDN'T BE ACTIVE
%QMRD::	JRST QRDFIN		;READ
%QMWRT::JRST QWRFIN		;WRITE
%QMWOV::JRST QWOVFN		;WRITE-OVER MODE
%QMRD1::JRST QRD1FN		;READ BLOCK AT A TIME MODE
	JRST 4,QINT1		;ILL CODE
%QMUDR::JRST QUDRIN		;USER DIR IN
%QMMDR::JRST QMDRIN		;MASTER DIR IN
%QMTTR::JRST QTUTIN		;TUT IN
%QMUDW::JRST QUFDWF		;USER DIR WRITE
%QMMDW::JRST QMFDWF		;MASTER DIR WRITE
%QMTTW::JRST QTUTWF		;TUT WRITE
%QMSWP::JRST QSWPFN		;SWAP FINISHED
	JRST 4,QINT1		;ILL CODE
	JRST 4,QINT1		;ILL CODE
	JRST 4,QINT1		;ILL CODE
%QMMAX::OFFSET 0
IFN %QMMAX-20, .ERR %QMMAX MUST BE A POWER OF 2

IFN RP10P,[
;CHANNEL DIDN'T TRANSFER EXACTLY 2000 WORDS
QDFLOS:	MOVEI D,1777(R)		;EXPECTED TERMINATION ADDRESS
	HRRZ B,QICWA+1		;ACTUAL TERMINATION ADDRESS
	CAIL B,(D)
	 SKIPL QSCRW(C)
	  JRST QDFLZ1
	BUG HALT,[DSK: CHANNEL TRANSFERRED UP TO ],OCT,B,[RATHER THAN],OCT,D,[DISK IS CLOBBERED NOW.]

QDFLZ1:	BUG PAUSE,[DSK: CHANNEL TRANSFERRED UP TO ],OCT,B,[RATHER THAN],OCT,D
	JRST QOVR
];RP10P

;;; SOFTWARE READ-COMPARE ROUTINES

IFN QRDCMP,[
IFE RP10P+RH10P, .ERR READ-COMPARE ONLY CODED FOR RP-10 AND RH10
;IFE KA10P, .ERR READ-COMPARE ONLY CODED FOR KA-10

;;; REGULAR OPERATION COMPLETE, DO A READ-COMPARE TO BE SURE
QRC1:
IFN T300P, CAIGE Q,T300P	;NO READ/COMPARE IF THIS IS T-300 CONTROLLER
	TLNE D,%QAPAR		;GIVING UP WITH FATAL ERR?
	 JRST QRC0		;NO READ-COMPARE THEN
	JUMPG TT,QRC2		;JUMP IF READ-COMPARE COMPLETED
	MOVE E,QSGL(C)		;DISK BLOCK
	PUSHJ P,QPOSR		;E GETS DATAO WORD
IFN RP10P,[
	IOR E,[DREADC+7000+RCICWA]	;MAKE READ COMMAND
	CONO DPC,175700+DSKCHN
	MOVE T,[-2000,,QRCBUF-1]	;STORE CHANNEL PROGRAM
	MOVEM T,RCIOWD
	SETZM RCIOWD+1
	MOVEI T,RCIOWD
	MOVEM T,RCICWA
	SETZM RCICWA+1
	DATAO DPC,E		;START DISK
];RP10P
IFN RH10P,[
	MOVE T,[-2000_4,,QRCBUF-1]	;DF10-C CHANNEL PROGRAM
	MOVEM T,RCIOWD
	SETZM RCIOWD+1
	MOVEI T,RCIOWD
	MOVEM T,RCICWA
	SETZM RCICWA+1
	SWPUA			;DUMP CACHE
	CONSZ 200000		;IT'S INEFFICIENT, BUT WHAT THE HELL
	 JRST .-1
	CONO DSK,%HOCLR+%HORAE+%HOATN+DSKCHN
	MOVSI A,%HRADR(Q)
	HRR A,E			;HEAD AND SECTOR (CYL DOESN'T CHANGE)
	PUSHJ P,RHSET
	MOVEI A,%HMRED+<RCICWA_6>
	TLO A,%HRCTL(Q)
	PUSHJ P,RHSET		;START DISK
];RH10P
	MOVEM T,RDCPHS		;RDCPHS POSITIVE MEANS DOING RD/CMP NOW
	JRST DSKEX		;AWAIT COMPLETION

;;; READ-COMPARE COMPLETED.  CHECK IT.
QRC2:	SPM PARPG		;SET UP MAP TO PAGE BEING READ/WRITTEN
	MOVEI T,600000+PMCSHM(A)
	DPB T,[.PAREP+EXEUMP]
	LPMR PARPG
	MOVEI H,0		;H DIFFERENCE COUNT
IFN KA10P,[
	PUSH P,A		;WILL DO COMPARE LOOP IN ACS
	PUSH P,C
	PUSH P,D
	MOVSI E,-2000		;E ADDRESS WITHIN PAGE
	MOVSI D,[ MOVE T,400000+PAREP*2000(E)	;A
		  CAMN T,QRCBUF(E)		;B
		   AOBJN E,A			;C
		  JRST QRC3 ]			;D
	HRRI D,A
	BLT D,D
	JRST A
];KA10P
.ELSE [
	MOVSI E,-2000		;E ADDRESS WITHIN PAGE
	MOVE T,400000+PAREP*2000(E)
	CAMN T,QRCBUF(E)
QRC2A:	 AOBJN E,.-2
];NOT KA10P
QRC3:	JUMPGE E,QRC5		;JUMP IF DONE
	JUMPG H,QRC4		;COMPARE ERROR, REPORT IT
	AOS NQCMPE(Q)
IFN KA10P,[
	MOVE T,-1(P)		;SAVED C
	BUG INFO,[DSK: READ-COMPARE ERROR #],DEC,NQCMPE(Q),[UNIT=],DEC,Q,[BLK=],OCT,QSGL(T)
];KA10P
.ELSE	BUG INFO,[DSK: READ-COMPARE ERROR #],DEC,NQCMPE(Q),[UNIT=],DEC,Q,[BLK=],OCT,QSGL(C)
QRC4:	ADDI H,1
	HRRZ T,E
	CAIG H,4		;ONLY REPORT FIRST 4 BAD WORDS
	 BUG INFO,[DSK: READ-COMPARE DIFFERENCE ],OCT,T,OCT,400000+PAREP*2000(E),OCT,QRCBUF(E)
IFN KA10P,JRST C
.ELSE	JRST QRC2A

QRC5:
IFN KA10P,[
	POP P,D
	POP P,C
	POP P,A
];KA10P
	MOVEI T,0		;CLEAR MAP
	DPB T,[.PAREP+EXEUMP]
	LPMR PARPG
	JUMPE H,QRC0		;NO DIFFERENCE, I/O OPERATION IS DONE
	CAILE H,4		;FAILED, DO IT ALL OVER AGAIN
	 BUG INFO,[DSK: READ-COMPARE DIFFERENCES TOTAL],DEC,H
	JRST QOVR
];QRDCMP

QUFDWF:	MOVE TT,QDWIP
	CAMN Q,MDSK		;WRITTEN TO MASTER DISK?
	 SKIPN E,QSFBT(TT)	;AND HAVE SOME BLOCKS TO FREE?
	  JRST QUDWF1
	SETZM QSFBT(TT)		;YES, TRANSFER THEM TO QFBTS LIST
	MOVE T,E		;E FINDS LAST OF LIST, T RPLACD'D IN THERE
	EXCH T,QFBTS		;NCONC QSFBT ONTO FRONT OF QFBTS
	SKIPE 1(E)
	 JRST [	MOVE E,1(E)
		JRST .-1 ]
	MOVEM T,1(E)
	SKIPN T
	 AOS NCORRQ		;WAKE UP CORE JOB IF QFBTS HAD BEEN EMPTY
QUDWF1:	JSP D,QDWF
	    QSNLCN(TT)

QMFDWF:	JSP D,QDWF
	    QMDRO

QTUTWF:	AOS T,QSGL(C)		;NEXT BLOCK TO GO OUT
	AOS QSCABN(C)		;FROM NEXT CORE PAGE
	CAIGE T,MFDBLK		;SKIP IF DONE
	 JRST QINT1E		;WRITE MORE
	JSP D,QDWF
	    QTUTO(TT)

QDWF:	MOVSI E,(SETZ)		;UNLOCK DIR THAT WAS WRITTEN
	IOR E,DCHBT(Q)		;COPY ON THIS DISK IS NOW UP TO DATE
	MOVE TT,QDWIP
	ANDCAM E,@(D)
	HLLZS QSRAC(C) .SEE %QMIDL	;IDLE THE CHANNEL
	SETOM DWUSR
	JRST QINT1A		;DON'T COUNT THIS AS DISK ACTIVITY

QUDRIN:	MOVE TT,QUDPR(C)	;USER DIR IN OK
	MOVE D,UDNAME(R)
	CAME D,QSNUD(TT)	;COMPARE UNAME IN DIR WITH RQ'ED
	 JRST QUDER1
	TLO R,40000		;NOT PAWED OVER
	MOVEM R,QSNLCN(TT)
	MOVE R,QSLGL(C)
	MOVEM R,QSNMI(TT)
	MOVEI R,MU23UD		;USER DIRECTORY
	PUSH P,A
	PUSH P,C
	MOVE C,TT
	PUSHJ P,QDIRCK		;VERIFY NOT TOTALLY BASHED TO BEGIN WITH
	POP P,C			;CLOBBERS A,B,I
	POP P,A
QINTU4:	DPB R,[MUR,,MEMBLT(A)]
	DPB TT,[MNUMB,,MEMBLT(A)]
QTUTI1:	SETOM QUSR(C)		;FREE THE DISK CHANNEL
	AOS QFCHN
	JRST QINT1A

QMDRIN:	TLO R,2			;NOT RECONCILED
	MOVEM R,QMDRO		;MASTER DIRECTORY IN
	MOVE TT,MDCHK(R)
	CAME TT,[SIXBIT /M.F.D./]
	 BUG HALT,[MFD CLOBBERED]
	MOVE TT,MDNUDS(R)
	CAIE TT,NUDSL
	 BUG HALT,[MFD HAS WRONG NUMBER OF DIRECTORIES]
	MOVE TT,(R)		;ASCENDING DIR #
	SKIPGE QAMDNO
	 MOVEM TT,QAMDNO	;FIRST DISK IN STORE ASCENDING #
	MOVEI R,MU23MD		;MASTER DIRECTORY
	MOVE TT,Q		;DISK NO
	JRST QINTU4

QTUTIN:	AOS T,QSGL(C)		;NEXT BLOCK TO COME IN
	AOS QSCABN(C)		;INTO NEXT CORE PAGE
	CAIGE T,MFDBLK		;SKIP IF DONE
	 JRST QINT1E		;READ MORE
	HRRZS QTUTO(Q)		;UNLOCK TUT, BUT QDPWF IS STILL SET
	JRST QTUTI1

QSWPFN:	MOVE Q,QDSKN(C)
	TLNE D,%QAPAR
	 JRST QSWPF2		;SWAP READ ERROR (I HOPE)
	MOVSI E,MMPTMP		;PAGE NO LONGER IN TRANSIT
	TDNN E,@QSMMP(Q)
	 BUG			;ALREADY WASN'T?
	ANDCAM E,@QSMMP(Q)
	SKIPL QSCRW(C)
	 JRST QSWPIF
	PUSHJ P,IMEMR		;SWAP-OUT COMPLETE, RETURN MEMORY
	MOVSI E,MMPPGB
	ANDCAM E,@QSMMP(Q)
	MOVSI E,MMPWOD		;PAGE HAS BEEN WRITTEN ON DISK.  AND NO LONGER
	IORB E,@QSMMP(Q) 	;BEING PAGED BEHIND.
	TLNN E,MMPGON
	 JRST QSWPF2
	MOVE A,QSMMP(Q) 	;WE'RE SUPPOSED TO RETURN THE MMP ENTRY
	PUSHJ P,RETMMP
QSWPF2:	SETOM SWUSR(Q)		;SWAPPING IDLE
	JRST QINT1B

QSWPIF:
;SOME ERROR CHECKING
	MOVE E,QSMMP(Q)
	HLLZ TT,1(E)
	JUMPE TT,QSWPF3		;ON INITIAL-SWAP-IN, ADDRESS IN MMP IS SUPPOSED TO BE ZERO
	LDB TT,[$MMPUN,,(E)]
	CAME TT,Q
	 BUG
	HLRZ TT,1(E)
	CAME TT,QSGL(C)
	 BUG
QSWPF3:	LDB TT,[MMMPX,,MEMBLT(A)]
	CAIE TT,-MMP(E)
	 BUG
;END ERROR CHECKING
	MOVEI TT,MURUSR		;SWAP IN OF PAGE COMPLETED
	DPB TT,[MUR,,MEMBLT(A)]
	SETZM MMSWP(A)
	MOVE D,(E)
	SKIPL CIRPSW
	 JRST QSWPF2		;CAN'T PUT IN MAPS IF CIRPSW NOT AVAILABLE.
	MOVSI D,(TDNE T,)	;SET UP CHECK USED BY PRIVELEGED USER FEATURE
	HRR D,QSMMP(Q)
	PUSH P,C
	PUSH P,Q
	MOVE C,[2200,,MEMPNT(A)]
	PUSHJ P,PPIUM		;PUT PAGE INTO USERS' MAPS
	POP P,Q
	POP P,C
	JRST QSWPF2

QWOVFN:	CLEARM QSCRW(C)		;WRITE OVER SWITCH TO READ MODE
	HRRI D,%QMRD1
	TLZE D,%QAEFW
	 HRRI D,%QMIDL		;TRYING TO CLOSE, STOP CHANNEL
	MOVEM D,QSRAC(C)
	AOS QWBUFS		;COMPENSATE FOR EXTRA SOS BELOW

QWRFIN:	TLNN D,%QACTH		;WRITE COMPLETE, RETURN BUFFER
	 PUSHJ P,IMEMR
	TLNE D,%QACTH
	 PUSHJ P,CIMEMR
	SOS QSBFS(C)
	SOS QWBUFS
	MOVE TT,QSGL(C)
	MOVEM TT,QSLBLK(C)
	JRST QINT1B

QRD1FN:	HLLZS QSRAC(C) .SEE %QMIDL	;BLOCK-AT-A-TIME READ COMPLETE, IDLE THE CHANNEL

QRDFIN:	MOVEI B,.BM MLO		;NORMAL READ COMPLETED
	ANDCAM B,MEMBLT(A)	;THREAD ONTO CHANNEL LIST
	HLRZ B,QBFP(C)
	SKIPE B
	 DPB A,[MLO,,MEMBLT(B)]
	SKIPN B
	 MOVEM A,QBFP(C)
	HRLM A,QBFP(C)
	SKIPL B,QPIBSZ(C)	;GET BYTE COUNT OF BLOCK
	 JRST QRDFN1
	HRRZ B,1777(R)		;FUNNY BLOCK, GET FROM LAST WORD
	HRRZ TT,QSBYTE(C)	;AND MAKE SURE IT'S NOT TOO BIG
	IMULI TT,2000
	CAMLE B,TT
	 MOVE B,TT
QRDFN1:	HRRZM B,MEMPNT(A)	;PASS TO MAIN PROGRAM
	AOS QSBFS(C)
IFE DMDSK,	LDB B,[XWBLK QXWDS]
     .ALSO	MOVEM B,QSLBLK(C)

;XFER COMPLETE, DEACTIVATE CHANNEL

QINT1B:	SETOM QACTTM
QINT1A:	SETOM QSGL(C)
	SETOM QSCABN(C)
QINT1E:
IFE DC10P, SETOM QSKT1(Q)
IFN DC10P,[
	MOVE TT,QTRAN(Q)
	SETOM QSKT1(TT)
] ;DC10P
IFN T300P,[
	CAIL Q,T300P
	 SETOM QSDU1
	CAIGE Q,T300P
	 SETOM QSDU
];T300P
.ELSE	SETOM QSDU
;DROPS THROUGH
;DROPS IN

;ACTIVATE CHANNELS, LOOK FOR SEEKS AND TRANSFERS TO BE DONE

QINT1:	SETZM QHUNGF
	SETOM QTUNT		;NO UNIT SELECTED YET
IFN T300P, SETOM QTUNT1
	MOVE C,QTCHN		;START SCAN IN SUITABLE PLACE
	MOVEM C,QLCHN
	JRST QINT2D

QINT2L:	CAIGE C,NQCHN		;IF DIR-WRITE OR SWAP CHANNEL, DON'T CHECK QUSR
	 SKIPL QUSR(C)
	  SKIPGE D,QSRAC(C)	.SEE %QALOK
	   JRST QINT2C		;CHANNEL LOCKED OR NOT OPEN, SKIP IT
	MOVE Q,QDSKN(C)
	SKIPL E,QSGL(C)
	 JRST QINT4		;JUMP IF CHANNEL ACTIVE ALREADY
	TLNE D,%QACTH+%QAOUT
	 JRST QINT2C		;CHANNEL BLOCKED BY CORE JOB, DON'T ACTIVATE
	TRNE D,-%QMMAX
	 BUG
	XCT .+1(D)		;INVOKE ACTIVATE HANDLER
	OFFSET -.
%QMIDL::JRST QINT2C		;IDLE - DON'T ACTIVATE
%QMRD::	JRST QRDACT		;READ
%QMWRT::JRST QWRACT		;WRITE
%QMWOV::JRST QWRACT		;WRITE OVER
%QMRD1::JRST QRDACT		;READ 1 BLOCK
	JRST 4,QINT2C		;ILL CODE
%QMUDR::JRST QDRACT		;UFD READ
%QMMDR::JRST QDRACT		;MFD READ
%QMTTR::JRST QDRACT		;TUT READ
%QMUDW::JRST 4,QINT2C		;UFD WRITE - SHOULDN'T LEAVE THIS MODE AROUND
%QMMDW::JRST 4,QINT2C		;MFD WRITE - ..
%QMTTW::JRST QINT5		;TUT WRITE - MAY BE WRITING SECOND PAGE
%QMSWP::JRST SWPACT		;SWAP
	JRST 4,QINT2C		;ILL CODE
	JRST 4,QINT2C		;ILL CODE
	JRST 4,QINT2C		;ILL CODE
%QMMAX::OFFSET 0

; ACTIVATION ROUTINES

SWPACT:	SKIPL SWUSR(Q)
	 BUG			;SWAPPING ALREADY ACTIVE ON THIS DISK
	MOVE A,MEMFR
	SUB A,NCBCOM
	MOVEI B,0		;CHECK FIRST FOR SWAP IN OR OUT
	CAIGE A,6		;ACCORDING TO AVAIL MEM
	 MOVEI B,1
	SKIPN @SWAPL(B)		;IF NO TRAFFIC THAT DIRECTION, CHECK THE OTHER.
	 TRC B,1
	HRRZ E,@SWAPL(B)
	JUMPE E,QINT2C		;NO SWAPPING TRAFFIC THIS DISK.
	LDB A,[MLO,,MEMBLT(E)]	;TAKE BLOCK OFF LIST
	SKIPN A
	 SETZM @SWAPL(B)	;LAST BLOCK, LIST IS NOW EMPTY
	HRRM A,@SWAPL(B)	;SET NEW FIRST BLOCK
	LDB D,[MMMPX,,MEMBLT(E)]
	ADD D,MMPEAD		;GET MMP ADDRESS
	LDB TT,[410200,,(D)]
	TRNN TT,1
	 BUG			;THIS PAGE SHOULD BE MARKED IN TRANSIT
	HRRZM D,QSMMP(Q)
	SOS SILNG(B)
	CAIGE E,TSYSM
	CAIGE E,SYSB
	 BUG
	HRRZM E,QSCABN+NQCHN+1(Q)
	CLEARM SWUSR(Q)		;CHNL ACTIVE
	HLRZ E,MEMPNT(E)
	MOVEM E,QSGL+NQCHN+1(Q)	;DISK BLOCK
	HRRZS QSRAC+NQCHN+1(Q)	.SEE %QMSWP
	MOVNM B,QSCRW+NQCHN+1(Q) ;DIRECTION
	CLEARM QERRS+NQCHN+1(Q)	;NO ERRORS (YET)
	JRST QINT4		;CHANNEL SUCCESSFULLY ACTIVATED


; MORE ACTIVATION ROUTINES 

QRDACT:	TLNE D,%QAACC		;ACTIVATE READ CHANNEL
	 JRST QINT2C		;NOT IF USER HACKING RANDOM ACCESS NOW
	SETZM QERRS(C)
	MOVE J,QUDPR(C)
	SKIPGE QSNLCN(J)
	 JRST QINT2C		;USER DIRECTORY OUT OR LOCKED
	MOVE A,QSBFS(C)
	SKIPL QSMDN(C)
	 AOS A			;A := TOTAL NUMBER OF BUFFERS
	CAMLE A,QRDAHD		;HOW MUCH SHOULD BE READ AHEAD
	 JRST QINT2C		;BLOATED, DON'T ACTIVATE
	SKIPE QSBI(C)
	 JRST QINT4A		;BLOCKS LEFT FROM LAST TIME
QINT4B:	PUSHJ P,QIDRCH		;GET NEXT DESC BYTE IN A, ALSO RET BYTE PNTR IN TT
	CAIN A,UDWPH
	 JRST QINT4B
	JUMPE A,QEOF		;REACHED READ EOF
	TRZE A,40
	 JRST QINT4C
	CAILE A,UDTKMX
	 JRST QINT4D		;SKIP AND TAKE
	MOVEM A,QSBI(C)		;TAKE NEXT N
QINT4A:	SOS QSBI(C)
	AOS E,QSLGL(C)
QINT4G:	MOVEM E,QSGL(C)
	MOVSI B,%QALBK
	ANDCAM B,QSRAC(C)	;CLEAR PROCESSING LAST BLOCK IN FILE BIT
	HRRZ A,QSBYTE(C)
	IMULI A,2000		;NUMBER OF BYTES IN A FULL BLOCK
IFN DMDSK,[
	MOVSI D,%QAFNY		;FUNNY FILE
	TDNE D,QSRAC(C)
	 SETO A,
]
	MOVEM A,QPIBSZ(C)	;SET FUNNY BIT
	SKIPE QSBI(C)
	 JRST QINT4		;IF MORE BLOCKS FOLLOW DON'T CHECK EOF
	PUSHJ P,QIDRCH		;GET NEXT DESCR BYTE
	SOS QDIRP(C)		;CORRECT PNTR
	JUMPN A,QINT4
	MOVE A,QUDFPR(C)	;THIS IS LAST BLOCK
	ADD A,QSNLCN(J)
	LDB D,[UNBYTE+UNREF(A)]
	PUSHJ P,QBDCD
	IMUL D,E		;NUMBER OF UNUSED BITS IN LAST WORD
	LDB E,[QSBSIZ(C)]	;(DEPEND ON DIVIDE ROUNDING DOWN)
	IDIV D,E		;NUMBER OF UNUSED BYTES (IN SIZE OPEN)
	LDB E,[UNWRDC+UNRNDM(A)];NUMBER OF USED WORDS IN LAST BLOCK
	SKIPN E
	 MOVEI E,2000
	IMULI E,@QSBYTE(C)	;CONVERT TO NUMBER OF BYTES
	SUB E,D			;NUMBER OF VALID BYTES IN BLOCK
	HRRM E,QPIBSZ(C)	;STORE BYTE COUNT OF LAST BLOCK
	IORM B,QSRAC(C) .SEE %QALBK
	MOVE E,QSGL(C)
	JRST QINT4

QINT4D:	MOVEI E,1-UDTKMX(A)	;SKIP N AND TAKE 1
	ADDB E,QSLGL(C)
	JRST QINT4G

QWRACT:	SKIPN I,QBFP(C)		;ACTIVATE WRITE CHANNEL
	 JRST QINT2C		;END OF WRITE LIST FOR NOW
	HRRZM I,QSCABN(C)
	LDB H,[MLO,,MEMBLT(I)]
	HRRM H,QBFP(C)
	SKIPN H
	 SETZM QBFP(C)		;LAST BLOCK, LIST NOW EMPTY
	HLRZ E,MEMPNT(I)
	MOVEM E,QSGL(C)
	JRST QINT4

QDRACT:	MOVE E,QSLGL(C)		;ACTIVATE DIRECTORY-READ CHANNEL
	MOVEM E,QSGL(C)
;	JRST QINT4		;DROPS THROUGH
;DROPS IN
QINT4:	SKIPL QSCABN(C)		;SKIP IF NEED MEMORY
	 JRST QINT5
	PUSHJ P,IOMQ		;GET MEMORY FOR READ BUFFER
	 JRST QINT2C		;CAN'T
QINT5A:	MOVEM A,QSCABN(C)
	MOVEI D,MU23B
	DPB D,[MUR,,MEMBLT(A)]
	DPB C,[MNUMB,,MEMBLT(A)]

QINT5:
IFN DC10P, MOVE TT,QTRAN(Q)
.ELSE	   MOVE TT,Q
	SKIPN QSEEK(TT)		;IGNORE SEEKING DRIVE
	 SKIPGE QRCAL(TT)	;IGNORE RECALIBRATING DRIVE
	  JRST QINT2C
IFN T300P,[			;IGNORE IF FOR CONTROLLER THAT IS NOT READY
	CAIL TT,T300P
	 JRST [	SKIPGE QSDU1
		 SKIPL QTUNT1	;NOTE CODE HERE IS SIMILAR TO THAT AT QINT2F
		  JRST QINT2C	;T-300 CONTROLLER BUSY OR ALREADY COMMITTED
		HRRZM Q,QTUNT1	;WILL TRANSFER ON THIS UNIT
		HRRZM C,QTCHN1	;FOR THIS CHANNEL
		JRST QINT2C ]	;T-300 CODE IS SIMPLIFIED SINCE NO SEEK OVERLAPS
	SKIPL QSDU
	 JRST QINT2C
];T300P
	HRRZ B,QSKT1(TT)
	CAIN B,(C)
	 JRST QINT2F		;ALREADY SET FOR ME
	SKIPL QSKT1(TT)
	 JRST QINT2C		;SET FOR SOME OTHER CHNL
	HRRZM C,QSKT1(TT)	;AVAILABLE, SET IT FOR ME
	PUSHJ P,QPOSR		;CONVERT DISK ADDRESS TO PHYSICAL
IFN DC10P,[			;AND INITIATE SEEK
	ADD E,[DSEEK]
	CONSZ DC0,DSSRUN+DSSACT
	 JRST .-1
	DATAO DC0,E
	LDB B,[DCYL E]
	MOVE TT,QTRAN(Q)	;PRETEND SEEK ALREADY COMPLETE
	MOVEM B,QPOSGL(TT)
	MOVEM B,QPOS(TT)
	JRST QINT2F		;DON'T WAIT FOR SEEK, START TRANSFER RIGHT AWAY
] ;DC10P
IFN RP10P,[
	TLO E,(DSEEKC)		;DSK SEEK
	CONSZ DPC,20
	 JRST 4,.-1
	LDB TT,[DCYL E]
	TRNE E,.BM DCYLXB
	 ADDI TT,400
	CAMN TT,QPOS(Q)
	 JRST QINT2F		;IF WE'RE THERE, DON'T SEEK
	MOVEM TT,QPOSGL(Q)
	DATAO DPC,E
	MOVEM E,QSEEK(Q)
	JRST QINT2C		;THIS ONE SEEKING, GO GET ANOTHER
] ;RP10P
IFN RH11P,[
	PUSHJ P,RHSLCT		; Select drive
	HRRZ A,E
	IOWRQ A,%HRADR		; Set track and sector
	HLRZ A,E
	IOWRQ A,%HRCYL		; Set cylinder
	CAMN A,QPOS(Q)
	 JRST QINT2F		; On cylinder, don't seek
	MOVEM A,QPOSGL(Q)
	MOVEI A,%HMSEK
	PUSHJ P,RHCMD		; Start seeking
	SETOM QSEEK(Q)
	JRST QINT2C		; Start other drives now?
] ;RH11P
IFN RH10P,[
	MOVSI A,%HRCYL(Q)	;STORE ADDRESS IN DRIVE
	HLR A,E
	PUSHJ P,RHSET
	MOVSI A,%HRADR(Q)
	HRR A,E
	PUSHJ P,RHSET
	HLRZ TT,E		;GET CYLINDER PART OF ADDRESS
	CAMN TT,QPOS(Q)
	 JRST QINT2F		;ON CYLINDER, DON'T SEEK
	MOVEM TT,QPOSGL(Q)
	MOVSI A,%HRDCL(Q)	;START SEEK
	HRRI A,%HMSEK
	PUSHJ P,RHSET
	SETOM QSEEK(Q)
	JRST QINT2C		;THIS ONE SEEKING, START OTHER DRIVES
] ;RH10P

IFN KL10P,[
;CALL HERE TO SWEEP THE CACHE.  CORE PAGE # IN R.
;INSTRUCTION AT CALL+1 SHOULD SKIP IF READING INTO CORE.
;CLOBBERS A,B,D,E,TT.

CSHSWP:	SETZB A,B		;A COUNTS WAIT TIME, B IS SWEEP INSTRUCTION
	XCT @(P)
	 TLOA B,(SWPUO (R))	;WRITE - UNLOAD PAGE FROM CACHE
	  MOVSI B,(SWPIO (R))	;READ - CLEAR PAGE FROM CACHE
	LSH R,1			;HARDWARE PAGES ARE 1/2 K
	XCT B			;SWEEP FIRST HALF-PAGE
	MOVE D,[CONSZ 200000]
	MOVE E,[AOJA A,D]
	MOVSI TT,(POPJ P,)
	PUSHJ P,D		;WAIT IN ACS TO MINIMIZE MBOX INTERFERENCE
	AOS R			;SWEEP SECOND HALF-PAGE
	XCT B
	PUSHJ P,D
	XCT @(P)
	 AOSA NCSHU		;COUNT NUMBER OF TIMES THIS DONE
	  AOSA NCSHI
	   JRST [ ADDM A,NCSHUL ? JRST .+2 ]
	  ADDM A,NCSHIL		;AND COUNT NUMBER OF LOOPS IN ACS
	LSH R,-1
	JRST POPJ1
]

;VARIOUS EXITS FROM CHANNEL-CHECKING ROUTINES

QINT2F:	SKIPL QTUNT		;THIS CHANNEL IS READY TO TRANSFER
	 JRST QINT2C		;ALREADY FOUND A TRANSFER
	HRRZM Q,QTUNT		;WILL TRANSFER ON THIS UNIT
	HRRZM C,QTCHN		;FOR THIS CHANNEL
QINT2C:	CAMN C,QLCHN		;CONTINUE CHANNEL SCAN
	 JRST QINT2E		;UNLESS CHECKED ALL CHANNELS
QINT2D:	CAIL C,NQCHN+1+NQS-1	;SKIP IF NOT TIME TO WRAP AROUND
	 SETO C,
	AOJA C,QINT2L		;CHECK ANOTHER

QINT2E:
IFN T300P,[
	MOVE C,QTCHN1
	SKIPL Q,QTUNT1		;FOUND XFER FOR T-300?
	 JRST QDE1		;YES
];T300P	
	SKIPGE Q,QTUNT		;FOUND XFER?
	 JRST QINT3		;IDLE
	MOVE C,QTCHN
;Q DISK TO TRANSFER ON, C CHANNEL.  START (OR RESTART) TRANSFER ON THEM.
QDE1:	MOVE R,QSCABN(C)
IFN KL10P,[
	PUSHJ P,CSHSWP
	  SKIPGE QSCRW(C)
];KL10P
IFN KS10P,[
	SKIPL QSCRW(C)
	 CLRCSH
];KS10P
IFE T300P, MOVEM C,QSDCH
IFN T300P,[
	CAIL Q,T300P
	 JRST T3IO		
	CAIGE Q,T300P
	 MOVEM C,QSDCH
];T300P
	SKIPGE B,QSCRW(C)	;LOAD R/W STATUS IN B
	 JRST QINT6W
;READ - DROPS IN
IFN RP10P,[
	MOVSI T,(DREADC)
QINT6A:	IORI T,7000+QICWA	;DON'T STOP FOR PARITY ERRORS (PLUS INITIAL CHNL ADR)
	MOVEM T,QCHPRG
	DPB Q,[DUNFLD+QCHPRG]
	HLLZS QIOWD
	DPB R,[121000,,QIOWD]
	LDB E,[100300,,R] 	;MA15-17
	TRC E,7
	DPB E,[410300,,QIOWD]	;SET UP HIGH ADDR BITS.
	MOVE TT,QIOWD
	SOS TT
	HRRM TT,QIOWD
	SETZM QIOWD+1
	MOVEM Q,QSDU
	MOVE E,QSGL(C)
	PUSHJ P,QPOSR
	IORM E,QCHPRG
QOVR:	CONSZ DPC,20
	 JRST QOVR
	SKIPGE Q,QSDU
	 BUG
	CONO DPC,175700+DSKCHN
IFN QRDCMP,[
	SETZM RDCPHS		;ASSUME NO READ-COMPARE WANTED
	SKIPE QRCSW
	 SETOM RDCPHS		;REMEMBER TO READ-COMPARE LATER
];QRDCMP
	MOVEI TT,QIOWD
	HRRZM TT,QICWA
	MOVE E,QCHPRG
	LDB TT,[DCYL E]
	TRNE E,.BM DCYLXB
	 ADDI TT,400
	CAME TT,QPOS(Q)
	 BUG			;DONT DO ANYTHING IF NOT WHERE YOU SHOULD BE
	SETZM QICWA+1
	DATAO DPC,QCHPRG	;ENTRY ON OVERRUN
	MOVE A,TIME
	MOVEM A,LQTM
	MOVEM Q,QSDU
	CAILE C,NQCHN
	 JRST .+5
	  SKIPL B
	   AOSA NRXFR
	    AOS NWXFR
	  JRST QINTX
	SKIPL B
	 AOSA NSRXFR
	  AOS NSWXFR
QINTX:	JRST DSKEX

QHUNG:	CONO DPC,DCLEAR+20+DSKCHN	;DISK NOTICED TO BE HUNG, RESET IT
	SETOM QHUNGF			;TELL P.I. LEVEL TO RETRY OPERATION
	MOVE Q,QSDU
	AOS NTQHNG(Q)
	BUG INFO,[DSK: HUNG ON UNIT ],DEC,QSDU,[QCHPRG=],OCT,QCHPRG
	POPJ P,
]
;READ - DROPS IN

IFN RH11P,[
	MOVEI T,%HMRED
QINT6A:	MOVEM T,QCHPRG
	MOVEM Q,QSDU
	LSH R,1			; Set up Unibus map for RH11 to point at
	TRO R,%UQVAL+%UQFST	;  the block in question.
	IOWRQ R,UBAPAG+QUBPG_1
	ADDI R,1
	IOWRQ R,UBAPAG+QUBPG_1+1
	MOVE E,[-4000,,QUBPG_14]
	MOVEM E,QIOWD
	MOVE E,QSGL(C)
	PUSHJ P,QPOSR
	MOVEM E,QCHPGA
QOVR:	IORDQ TT,%HRCS1		; Enter here to recover from PI level lossage
	TRNN TT,%HXRDY
	 JRST QOVR
	SKIPGE Q,QSDU
	 BUG
QECCX:	;; Enter here from ECC correction code
	PUSHJ P,RHCLRC		; Clear controller errors and select drive
	HLRZ TT,QCHPGA
	CAME TT,QPOS(Q)
	 JRST DSKEX		; Punt if not positioned in right place
	IOWRQ TT,%HRCYL		; Store cylinder in drive
	HRRZ TT,QCHPGA
	IOWRQ TT,%HRADR		; Store track and sector
	HLRZ TT,QIOWD
	IOWRQ TT,%HRWC		; Store halfword count
	HRRZ TT,QIOWD
	IOWRQ TT,%HRBA		; Store Unibus base address
	MOVE A,QCHPRG
	PUSHJ P,RHCMD		; Go!
	MOVE A,TIME
	MOVEM A,LQTM
	CAILE C,NQCHN
	 JRST .+5
	  SKIPL B
	   AOSA NRXFR
	    AOS NWXFR
	  JRST QINTX
	SKIPL B
	 AOSA NSRXFR
	  AOS NSWXFR
QINTX:	JRST DSKEX

QHUNG:	MOVEI A,%HYCLR		; Sock controller in jaw
	IOWRQ A,%HRCS2
	MOVE Q,QSDU
	PUSHJ P,RHSLCT		; Select drive
	PUSHJ P,RHCLRD		; Redundantly clear drive
	SETOM QHUNGF
	AOS NTQHNG(Q)
	BUG INFO,[DSK: HUNG ON UNIT #],DEC,QSDU,[ADDR=],OCT,QCHPGA
	POPJ P,

] ;RH11P
;READ - DROPS IN

IFN RH10P,[
	MOVEI T,%HMRED
QINT6A:	IORI T,QICWA_6	.SEE $HCICWA
	TLO T,%HRCTL(Q)
	MOVEM T,QCHPRG
	MOVEM Q,QSDU
	LSH R,10.		;ASSEMBLE DF10-C CONTROL WORD
	SUBI R,1
	MOVNI E,2000
	DPB E,[$DFWC R]
	MOVEM R,QIOWD
	SETZM QIOWD+1
	MOVE E,QSGL(C)
	PUSHJ P,QPOSR
	MOVEM E,QCHPGA
QOVR:	CONSZ DSK,20		;ENTER HERE FOR RECOVER FROM PI HALT, OVERRUN
	 JRST QOVR
	SKIPGE Q,QSDU
	 BUG
	MOVEI TT,QIOWD
	HRRZM TT,QICWA
QECCX:	SETZM QICWA+1		;ENTER HERE FROM ECC CORRECTION CODE
	CONO DSK,%HOCLR+%HORAE+%HOATN+DSKCHN
	HLRZ TT,QCHPGA
	CAME TT,QPOS(Q)
	 JRST DSKEX		;DONT DO ANYTHING IF NOT WHERE YOU SHOULD BE
	MOVSI A,%HRCYL(Q)	;STORE ADDRESS IN DRIVE
	HLR A,QCHPGA
	PUSHJ P,RHSET
	MOVSI A,%HRADR(Q)
	HRR A,QCHPGA
	PUSHJ P,RHSET
	MOVE A,QCHPRG
	PUSHJ P,RHSET
IFN QRDCMP,[
	SETZM RDCPHS		;ASSUME NO READ-COMPARE WANTED
	SKIPE QRCSW
	 SETOM RDCPHS		;REMEMBER TO READ-COMPARE LATER
];QRDCMP
	MOVE A,TIME
	MOVEM A,LQTM
	CAILE C,NQCHN
	 JRST .+5
	  SKIPL B
	   AOSA NRXFR
	    AOS NWXFR
	  JRST QINTX
	SKIPL B
	 AOSA NSRXFR
	  AOS NSWXFR
QINTX:	JRST DSKEX

QHUNG:	CONO DSK,%HOCLR+%HORST+%HOSTP+DSKCHN	;HUNG, CLEAR CONTROLLER
	SETOM QHUNGF
	MOVE Q,QSDU
	AOS NTQHNG(Q)
	MOVSI A,%HRDCL(Q)
	HRRI A,%HMCLR		;CLEAR DRIVE
	PUSHJ P,RHSET
	BUG INFO,[DSK: HUNG ON UNIT ],DEC,QSDU,[ADDR=],OCT,QCHPGA
	POPJ P,
]
;READ - DROPS IN
IFN DC10P,[
	MOVSI T,(DREAD)
QINT6A:	MOVEM T,QCHPRG
	MOVEM Q,QSDU
	DPB R,[DCBN+QCHPR2]	;MEM BLOCK #
	DPB R,[DCBN+QCHPR3]	;FOR POSSIBLE READ COMPARE
	MOVE E,QSGL(C)
	PUSHJ P,QPOSR	;QPOSR ALSO STORES MAPPED UNIT IN QCHPRG
	IORB E,QCHPRG
	CLEARM QCHPR4	;STORE DHALT FOR NO RCC
	CAILE C,NQCHN
	 JRST QINT6S	;SWAPPING CHNL
	SKIPL B
	 AOSA NRXFR
	  AOS NWXFR
	CAIE C,NQCHN	;ALWAYS R COMPARE DIR WRITES
	 SKIPLE QRCSW	;SKIP ON NOT READ COMP EVERYTHING
	  JRST QINT6B	;RCC
	SKIPL QRCSW
	 JUMPL B,QINT6B	;RCC WRITES
	HRRZ D,QSRAC(C)
	CAIL D,%QMUDR
	CAILE D,%QMTTR
	 JRST QINT6C	;NOT DIR READ
QINT6B:	TLZ E,340000	;CHANGE TO READ COMPARE
	MOVEM E,QCHPR4
QINT6C:	SETOM QERS1	;ERR VERIFY IND
QOVR:	CONSZ DC0,DSSRUN+DSSACT
	 JRST QOVR
	DATAO DC0,[DJMP QCHPRG]	;ENTRY ON OVERRUN
QOVR1:	CONO DC0,DCSET+DCIENB+DSKCHN	;INTERRUPT WHEN DONE
	MOVE A,TIME
	MOVEM A,LQTM
QINTX:	JRST DSKEX

QINT6S:	SKIPL B
	 AOSA NSRXFR
	  AOS NSWXFR
	JUMPL B,QINT6B	;RCC WRITES
	JRST QINT6C	;NOT READS

QHUNG:	CONO DC0,DCCSET+DSKCHN	;HUNG, CLEAR CONTROLLER
	SETOM QHUNGF
	MOVE Q,QSDU
	AOS NTQHNG(Q)
	BUG INFO,[DSK: HUNG ON UNIT ],DEC,QSDU,[QCHPRG=],OCT,QCHPRG
	POPJ P,
]

QINT6W:IFE DMDSK,[
	MOVE T,[QXWDS-1,,QXWDS]
	BLT T,QXWDS+3
	CAIL C,NQCHN
	 JRST QNT6W2	;SWAP OR DIR WRITE
	MOVE A,QUDPR(C)
	MOVE T,QSNUD(A)
	MOVEM T,QXWDS+XWSYSN
	HRRZ A,QSNLCN(A)
	ADD A,QUDFPR(C)
	MOVE T,UNFN1(A)
	MOVEM T,QXWDS+XWFN1
	MOVE T,UNFN2(A)
	MOVEM T,QXWDS+XWFN2
	MOVE T,QSLBLK(C)
	DPB T,[XWBLK+QXWDS]
	LDB T,[MWC,,MEMBLT(R)]
	DPB T,[XWAWC+QXWDS]
QNT6W2:
]
IFN DC10P,	MOVSI T,(DWRITE)
IFN RP10P,	MOVSI T,(DWRITC)
IFN RH10P,	MOVEI T,%HMWRT
IFN RH11P,	MOVEI T,%HMWRT
	JRST QINT6A

IFN T300P,[
T3IO:	MOVEM C,QSDCH1		;THIS IS MORE OR LESS QINT6A FOR T-300
	MOVEM Q,QSDU1
	MOVEI A,%DMRED
	SKIPGE B,QSCRW(C)
	 MOVEI A,%DMWRT
T3IO1:	MOVE R,QSCABN(C)	;RE-ENTER HERE TO RETRY WITH COMMAND IN A
	LSH R,10.		;FIRST ADDRESS IN TRANSFER
	TLO R,730000		;12-BIT BYTES, START WITH FIRST BYTE IN WORD
	MOVSI E,-4		;SET UP BYTE POINTERS
	MOVEM R,DSCPNT(E)
	ADDI R,400
	AOBJN E,.-2
	MOVE D,QSGL(C)		;DO LIKE QPOSR
	CAIL D,NBLKS1
	 BUG
	IDIVI D,NBLKC1
	MOVEM D,DSCCYL
	IMULI E,SECBL1
	IDIVI E,NSECS1
	MOVEM E,DSCHED
	MOVEM TT,DSCSEC
	PUSHJ P,T3CMD
	CAILE C,NQCHN
	 JRST .+5
	  SKIPL B
	   AOSA NRXFR1
	    AOS NWXFR1
	  JRST QINTX
	SKIPL B
	 AOSA NSRXF1
	  AOS NSWXF1
	JRST QINTX

;START T-300, COMMAND IN A, DRIVE IN Q
T3CMD:	MOVEI TT,2561
	MOVEM TT,DSCCHK
	MOVE TT,TIME
	MOVEM TT,LQTM1
	MOVEI TT,-T300P(Q)
	MOVEM TT,DSCDRV
	HRRZM A,DSCCMD
	SETZM DSCDON
	MOVEI T,1
	MOVEM T,DSCREQ
	CONO DLC,100040+TTYCHN	;INTERRUPT 11
	POPJ P,

QHUNG1:	MOVE Q,QSDU1
	AOS NTQHNG(Q)
	BUG INFO,[DSK: HUNG ON T-300 UNIT ],DEC,QSDU
	MOVEI TT,5*60.*30.	;SHUT UP FOR FIVE MINUTES
	ADDM TT,LQTM1
	POPJ P,
];T300P

QDE:	MOVE C,QSDCH
IFN RP10P, PUSHJ P,QERSOFT
IFN DC10P, AOSE PKIDM
	 SKIPGE Q,QSDU
	  BUG
IFN DC10P,[
	TRNE TT,DCKSER
	 JRST .+3	;GET CKS ERR
	  TRNE TT,DRCER
	   AOS NQCMPE(Q) ;# COMPARE ERRORS [WITH NO OTHER ERROR]
];DC10P
IFN T300P,T3DE:		;ENTER HERE FOR ERROR ON T-300
	AOS NQDE(Q)
	SKIPL R,QSCRW(C)
	 AOSA NQRDE(Q)
	  AOS NQWDE(Q)
IFN DC10P,	JUMPL R,QERV1	;DO ANOTHER RD/COMP TO SEE IF OK ON DSK (IF WRITE)
QERV2:	AOS R,QERRS(C)
	TRNN R,10	;TRY 8 TIMES BEFORE AND AFTER REPOSITION
	 JRST QDE1
	TRNN R,1000
	 JRST QDE2	;TRY REPOSITION ONCE
QERV3:	MOVE D,QSRAC(C)
	TRNE D,-%QMMAX
	 BUG
	XCT .+1(D)	;INVOKE IRRECOV ERROR HANDLER
QERV:	OFFSET -.
%QMIDL::JRST 4,QERV+.	;IDLE CHANNELS SHOULDN'T GET HERE
%QMRD::	JRST QPE2D	;USER DATA
%QMWRT::JRST QDE1	;WRITE KEEP TRYING
%QMWOV::JRST QDE1	;..
%QMRD1::JRST QPE2D
	JRST 4,QERV+.	;ILL CODE
%QMUDR::JRST QUDER1
%QMMDR::JRST QDE1
%QMTTR::JRST QDE1
%QMUDW::JRST QDE1
%QMMDW::JRST QDE1
%QMTTW::JRST QDE1
%QMSWP::JRST QSWPER
	JRST 4,QERV+.	;ILL CODE
	JRST 4,QERV+.	;ILL CODE
	JRST 4,QERV+.	;ILL CODE
%QMMAX::OFFSET 0

QSWPER:	SKIPGE CIRPSW		;TRY XFER AGAIN IF CIRPSW NOT AVAILABLE
	SKIPGE QSCRW(C)
	 JRST QDE1		;DON'T TRY TO DO ANYTHING ABOUT WRITE ERRORS
	MOVE A,QSCABN(C)	;READ - GIVE ALL USERS OF PAGE PARITY ERR
	PUSH P,C
	PUSH P,Q
	MOVE C,[2200,,MEMPNT(A)]
	PUSHJ P,UCPRL
	  400000,,QSWER1
	LDB Q,[2200,,MEMPNT(A)]
	DPB Q,C			;REMOVE MEM FROM LOOP
	PUSHJ P,IMEMR		;AND GIVE BACK MEM
	POP P,Q			;ALTERNATIVELY, COULD LEAVE THE MEM BUT SET MMPBAD
	POP P,C			;TO INDICATE THAT THAT MEM COPY OF THE PAGE IS NO GOOD.
	MOVEI D,2
	DPB D,[410200,,@QSMMP(Q)]	;PAGE IS OUT
	JRST QPE2D

QSWER1:	PUSH P,T
	MOVSI T,%PJPAR
	IORM T,PIRQC(U)
	JRST POPTJ

IFN DC10P,[
QERV1:	SKIPN QCHPR4
	 JRST QERV2	;NOT SET FOR RCC
	CLEARM QERS1
QERL2:	CONSZ DC0,DSSRUN+DSSACT
	 JRST .-1
	DATAO DC0,[DJMP QCHPR4]
	JRST QOVR1

QERL1:	AOS Q,QERS1
	CAIGE Q,50.
	 JRST QERL2
	AOSA NQWIRE
QEROK:	 AOS NQWRE
	MOVE C,QSDCH
	MOVE Q,QSDU
	SETOM QERS1
	JRST QERV2
]
QDE2:	MOVEI R,1000
	MOVEM R,QERRS(C)	;CLOBBER QERRS
	JRST QREC		;AND TRY REPOSITIONING

IFN RP10P,[
QERSOFT:LDB A,[DCYL+QCHPRG]	;PARSE STARTING DISK ADDRESS
	LDB B,[DCYLXB+QCHPRG]
	LSH B,8
	IOR A,B
	LDB B,[DSURF+QCHPRG]
	LDB D,[DSECT+QCHPRG]
	SKIPN QERRS(C)		;PRINT ONLY ONCE, NOT ON RETRIES
	 BUG INFO,[DSK: SOFT ERR UNIT ],DEC,Q,[CYL ],DEC,A,[STARTING HEAD ],DEC,B,[SEC ],DEC,D,[CONI=],OCT,QERST,[DATAI=],OCT,QERDTI
	POPJ P,

QRECAT:	CONI DPC,A
	BUG INFO,[DSK: SEEK ERR DATAO=],OCT,QSEEK(Q),[CONI=],OCT,A,[DATAI=],OCT,E
	SETZM QSEEK(Q)
	JRST QREC
];RP10P

QHE:	MOVE C,QSDCH
IFN RP10P, PUSHJ P,QERSOFT
	SKIPGE Q,QSDU
	 BUG
IFN T300P,T3HE:		;ENTER HERE FOR ID ERROR ON T-300
	AOS NQHE(Q)
	AOS E,QERRS(C)
	CAIL E,5.
	 JRST QHE2
QREC:
IFN T300P,[
	CAIL Q,T300P
	 JRST [	SETOM QRCAL(Q)
		MOVEI A,%DMREC
		PUSHJ P,T3CMD
		MOVEM Q,QSDU1	;CONTROLLER IS TIED UP BY RECAL BECAUSE
		JRST QINT1 ]	;IT'S TOO DAMNED PSEUDO-INTELLIGENT
];T300P
IFN DC10P,[
	MOVE TT,QTRAN(Q)
QREC0:	DPB TT,[DUNFLD+QRECAL]
	SETOM QRCAL(TT)
	SETOM QSKT1(TT)
	CLEARM QSPPS(TT)
	CLEARM QPOSGL(TT)
	SETOM QPOS(TT)
	MOVEI T,10.		;5-SECOND RECALIBRATE TIMEOUT
	MOVEM T,QRCTIM(TT)
	DATAO DC0,QRECAL
	CONO DC0,DCSET+DCATEB+DSKCHN	;ENABLE ATTENTION
]
IFN RP10P,[
	DPB Q,[DUNFLD+QRECAL]
	SETOM QPOS(Q)
	DATAO DPC,QRECAL
	CLEARM QSPPS(Q)
	SETOM QSKT1(Q)
	SETOM QRCAL(Q)	;INDICATE RECALIBRATING THIS DISK
	CLEARM QPOSGL(Q)
]
IFN RH11P,[
	PUSHJ P,RHSLCT		; Select drive
	PUSHJ P,RHCLRD		; Clear errors
	MOVEI A,%HMREC
	PUSHJ P,RHCMD		; Recalibrate
	CLEARM QSPPS(Q)
	CLEARM QPOSGL(Q)
	SETOM QPOS(Q)
	SETOM QSKT1(Q)
	SETOM QRCAL(Q)
] ;RH11P
IFN RH10P,[
	MOVSI A,%HRDCL(Q)
	HRRI A,%HMCLR
	PUSHJ P,RHSET		;CLEAR ERROR OUT OF DRIVE.
	MOVSI A,%HRDCL(Q)
	HRRI A,%HMREC		;RECALIBRATE
	PUSHJ P,RHSET		;MAYBE SHOULD TRY OFFSET FIRST?
	CLEARM QSPPS(Q)
	CLEARM QPOSGL(Q)
	SETOM QPOS(Q)
	SETOM QSKT1(Q)
	SETOM QRCAL(Q)
]
	SETOM QSDU
	JRST QINT1	;LOOK FOR SOME OTHER TRANSFER

QHE2:	SKIPL QSCRW(C)	;HANG UP OR POSITIONING ERR (AFTER 5 TRIES)
	 JRST QERV3	;IF READ, PERFORM RECOVERY

QPE2D:	MOVSI R,%QAPAR	;IRRECOVERABLE ERROR
	IORM R,QSRAC(C)
	AOS QIRRCV
	MOVE D,QSGL(C)
	MOVEM D,QIRCBK	;BLOCK # AT IRRCV ERR
	MOVEM Q,QIRUNT	;SAVE UNIT TOO
	CAILE C,NQCHN
	 AOS NIRSWE	;# IRRCV SWAPPING ERRS
	MOVE I,Q
	SKIPGE QTUTO(I)
	 JRST QPE2E	;DON'T MESS WITH LOCKED TUT
	PUSHJ P,TUTPNT
	CAIN B,TUTLK	;PRINT MESSAGE IF NOT YET LOCKED OUT
	 JRST QINTI
	MOVEI B,TUTLK
	DPB B,D
QPE2E:	BUG INFO,[DSK: IRREC DATA ERR #],DEC,QIRRCV,[UNIT=],DEC,QIRUNT,[BLK=],OCT,QIRCBK
	JRST QINTI

QUDER1:	MOVEI R,1(Q)	;TRY TO READ DIR FROM OTHER DISKS
QUDER2:	CAIL R,NQS
	 MOVEI R,0
	CAME R,QDSKN(C)
	 JRST QUDER4
	JRST QDE1	;NO OTHER DISK AVAIL TO READ FROM, TRY AGAIN

QUDER4:	SKIPGE QACT(R)
	 AOJA R,QUDER2
	HRRZM R,QDSKN(C)	;TRY THIS DISK
IFN T300P,[
	CAIL Q,T300P
	 JRST [	SETOM QSDU1	;FREE DRIVE FORMERLY HACKING
		SETOM QTUNT1
		JRST T3UDE4 ]
];T300P
IFE DC10P, SETOM QSKT1(Q)	;FREE DRIVE FORMERLY HACKING
IFN DC10P,[
	MOVE TT,QTRAN(Q)
	SETOM QSKT1(TT)
]
	SETOM QSDU
	SETOM QTUNT
IFN T300P,T3UDE4:
	MOVE E,QSGL(C)
	MOVE Q,QDSKN(C)
	SETZM QERRS(C)
	JRST QINT5

QIDRCH:	MOVE TT,QDIRP(C)	;CHNL IN C PNTR TO QSNUD IN J LOAD NEXT CHR INTO A
	AOS QDIRP(C)		;ALSO RET BYTE PNTR IN TT
	IDIVI TT,UFDBPW
	HLL TT,QBTBL(I)
	HRRZ I,QSNLCN(J)
	ADDI TT,UDDESC(I)
	LDB A,TT
	POPJ P,

QMPDCH:	MOVE TT,QDIRP(A)	;CHNL IN A PNTR TO QSNUD IN H LOADS NEXT CHR IN R
	AOS QDIRP(A)		;USED AT M.P. LEVEL
QMPDC1:	IDIVI TT,UFDBPW		;ALSO RETN BYTE PNTR IN TT
	HLL TT,QBTBL(I)
	HRRZ I,QSNLCN(H)
	ADDI TT,UDDESC(I)
	LDB R,TT
	POPJ P,

;REACHED EOF ON READ
QEOF:	SOS QDIRP(C)		;AVOID GC UNHAPPINESS
	MOVSI I,%QAEFR
	IORM I,QSRAC(C)
	HLLZS QSRAC(C) .SEE %QMIDL ;IDLE THE CHANNEL
	AOS QSBFS(C)		;TO UNHANG M.P. WHICH IS WAITING FOR BUF TO APPEAR
	JRST QINT2C

QINT4C:
IFN DMDSK,[
	MOVSI E,%QAFNY
	ANDCAM E,QSRAC(C)
	TRZE A,20	; FUNNY FILE BLOCK
	 IORM E,QSRAC(C); INDICATE SO
]
	MOVEI E,0
	DPB A,[140500,,E]
	PUSHJ P,QIDRCH
	DPB A,[060600,,E]
	PUSHJ P,QIDRCH
	DPB A,[0600,,E]
	MOVEM E,QSLGL(C)
	JRST QINT4G

;DECODE UNBYTE SPEC IN D.
;RETURNS BYTE SIZE IN D, NUMBER OF UNUSED BYTES IN LAST WORD IN E.
QBDCD:	TRZE D,400
	 JRST [ IDIVI D,100 ? POPJ P, ]
	TRZE D,200
	 JRST [ IDIVI D,20 ? POPJ P, ]
	SUBI D,44
	JUMPGE D,[ IDIVI D,4 ? POPJ P, ]
	MOVNS D
	SETZ E,
	POPJ P,

;ENCODE BYTE SIZE IN Q AND RESIDUE IN R INTO UNBYTE SPEC IN RH(Q)
QBENC:	CAIG Q,3
	 JRST [ IMULI Q,100 ? ADDI Q,400(R) ? POPJ P, ]
	CAIG Q,7
	 JRST [ IMULI Q,20 ? ADDI Q,200(R) ? POPJ P, ]
	CAIG Q,18.
	 JRST [ IMULI Q,4 ? ADDI Q,44(R) ? POPJ P, ]
	MOVNI Q,-44(Q)
	POPJ P,

QINT3:	SKIPL DWUSR
	 JRST QINT3X	;DIR CHNL IN USE
IFN T300P,[
	SKIPGE QSDU	;DON'T GET PAST HERE UNLESS BOTH CONTROLLERS ARE IDLE
	 SKIPL QSDU1
	  JRST QINT3X
];T300P
	AOSL QDWFAR
	 JRST [	MOVNI H,10.
		MOVEM H,QDWFAR
		JRST .+3 ]
	   SKIPG QACTTM	;LAST ACTIVITY TOO RECENT
	    JRST QUDW
	SKIPGE H,QMDRO
	 JRST QTDW
	MOVE J,QACTB
	TDNN J,H
	 JRST QTDW
	MOVSI Q,-NQS
QMDW:	SKIPGE QACT(Q)
	 JRST QMDWA	;UNIT NOT ACTIVE
	MOVE J,DCHBT(Q)
	TDNN J,QMDRO
	 JRST QMDWA
	HRLI Q,(SETZ)	;INDICATE MFD WRITE IN PROGRESS
	MOVEM Q,QDWIP
	HRRZM Q,DWSKN
	MOVEI TT,%QMMDW
	HRRZM TT,QSRAC+NQCHN
	MOVSI TT,(SETZ)
	IORB TT,QMDRO
	MOVE C,MDCHK(TT)
	CAME C,[SIXBIT /M.F.D./]
	 BUG HALT,[MFD CLOBBERED]
	HRRZ C,Q
	CAMN C,MDSK
	 AOSA C,QAMDNO
	  MOVE C,QAMDNO
	MOVEM C,MDNUM(TT)
	SETZM DWUSR
	MOVEI TT,MFDBLK
	MOVEM TT,QSGL+NQCHN
	HRRZ TT,QMDRO
	LSH TT,-10.
	MOVEM TT,QSCABN+NQCHN
	JRST QINT1

QMDWA:	AOBJN Q,QMDW
QTDW:	SKIPG QACTTM
	 JRST QUDW
	MOVSI Q,-NQS
QTDW1:	SKIPGE QACT(Q)
	 JRST QTDWA
	MOVE J,DCHBT(Q)
	SKIPL QTUTO(Q)
	TDNN J,QTUTO(Q)
	 JRST QTDWA
	MOVSI H,240000
	TDNE H,QTUTO(Q)
	 JRST QTDWA1
	MOVE TT,TIME		;DON'T WRITE TUTS TOO OFTEN
	SUB TT,QTWRTM(Q)	;BECAUSE THE TUT IS LOCKED WHILE IT'S BEING WRITTEN
	CAIGE TT,100.
	 JRST QTDWA
	ADDM TT,QTWRTM(Q)
	HRLI Q,200000
	MOVEM Q,QDWIP
	HRRZM Q,DWSKN
	MOVEI TT,%QMTTW
	HRRZM TT,QSRAC+NQCHN
	MOVSI TT,(SETZ)
	IORB TT,QTUTO(Q)
	MOVE H,QPKNM(Q)		;VERIFY THAT TUT IS NOT BEING CLOBBERED
	MOVE C,QPKID(Q)
	CAMN H,QPAKID(TT)
	 CAME C,QPKNUM(TT)
	  BUG HALT,[TUT ],DEC,Q,[CLOBBERED]
	SETZM DWUSR
	MOVEI TT,MFDBLK		;INITIATE WRITING OF FIRST BLOCK OF TUT
	SUB TT,NTBL(Q)
	MOVEM TT,QSGL+NQCHN
	LDB TT,[121000,,QTUTO(Q)]
	MOVEM TT,QSCABN+NQCHN
	JRST QINT1

QTDWA1:	ANDCAM J,QTUTO(Q)
QTDWA:	AOBJN Q,QTDW1
QUDW:	MOVSI C,-QNUD
QDW4A:	MOVE J,QACTB	;BITS CORRESP TO ACTIVE DISKS
QDW4:	SKIPE TT,QSNUD(C)
	SKIPGE QSNLCN(C)
	 JRST QDW3	;SLOT VACANT OR LOCKED
	TDNE J,QSNLCN(C)
	 JRST QUDW1	;NEEDS TO BE WRITTEN ON SOME UNIT
QDW3:	AOBJN C,QDW4A
QINT3X:	;HERE IF DISK GOING IDLE.  CLEAR DONE FLAG.
	;IN 2-CONTROLLER CASE, MAKE SURE WE ONLY DO IT TO THE RIGHT CONTROLLER.
IFN T300P,[
	SKIPL QSDU
	 JRST DSKEX	;STUFF NEEDS TO BE DONE BUT CANT NOW
] ;T300P
IFN DC10P, CONO DC0,DCCLR+DCIENB+DSKCHN
IFN RP10P, CONO DPC,177710+DSKCHN	;MUST CLEAR "DONE"
IFN RH10P, CONO DSK,%HOCLR+%HOATN+%HORAE+DSKCHN
IFN RH11P, ;; RH11 doesn't need this?
	JRST DSKEX	;STUFF NEEDS TO BE DONE BUT CANT NOW

QUDW1:	MOVSI J,%QUDWM	;WRITE RIGHT AWAY IF %QUDWM IS ON
	TDNN J,QSNLCN(C)
	 SKIPE QSFBT(C)	;OR DISK BLOCKS (AND CORE) WAITING TO BE FREED
	  JRST QUDW6
	SKIPG QACTTM	; ACTIVITY TOO RECENT
	 SKIPN QSNNR(C)	; THEN ONLY WRITE IF NOTHING POINTING TO DIRECTORY
	  SKIPA
	   JRST QDW3
	MOVSI Q,-NQS
QUDW4:	SKIPGE QACT(Q)
	 JRST QUDW3
	MOVE J,DCHBT(Q)
	TDNE J,QSNLCN(C)
	 JRST QUDW2
QUDW3:	AOBJN Q,QUDW4
	JRST QDW3

QUDW6:	ANDCAM J,QSNLCN(C)	;TURN OFF %QUDWM
	MOVE Q,MDSK		;AND WRITE ON MASTER DISK (ASSUME MDSK IS ACTIVE AND BIT IN QSNLCN IS SET)

QUDW2:	MOVE I,QMDRO
	TLNE I,40000	;IS THIS CODE OBSOLETE? LEFT FROM DAYS OF 1 MFD PER DRIVE?
	 JRST QUDW2B	;MASTER DIR NOT IN
	HRRZS Q
	CAMN Q,MDSK	;SKIP IF NOT WRITING ON MASTER DISK
	 PUSHJ P,QDIRCK	;BLESS THIS UFD!!!
	MOVSI TT,(SETZ)
	IORB TT,QSNLCN(C)	;LOCK USER DIRECTORY
	MOVEI J,%QMUDW
	HRRZM J,QSRAC+NQCHN
	MOVE A,1(TT)		;NAME AREA PTR
	MOVE J,QSNUD(C)
	CAMN J,UDNAME(TT)	;MAKE SURE NOT ABOUT TO WRITE BAD DIRECTORY
	 CAILE A,2000
	  JSP TT,QUDCLB
	SUBI A,11.
	IMULI A,6
	SKIPL J,(TT)
	CAMLE J,A	;FS PTR BAD?
	 JSP TT,QUDCLB
	CLEARM DWUSR
	MOVEM Q,DWSKN
	MOVE TT,QSNMI(C)
	MOVEM TT,QSGL+NQCHN
	HRRZ TT,QSNLCN(C)
	LSH TT,-10.
	MOVEM TT,QSCABN+NQCHN
	HRRZM C,QDWIP
	CLEARM QERRS+NQCHN
	JRST QINT1

QUDW2B:	ANDCAM J,QSNLCN(C)	;CLEAR BIT + GO AWAY
	JRST QUDW3

;GENERALLY GET HERE BY JSP TT,
QUDCLB:	BUG HALT,[DIR ],OCT,C,SIXBIT,QSNUD(C),[CLOBBERED]

QDIRCK:	PUSH P,TT	;CHECKS FILES FOR REASONABLE DESCRIPTORS -
			; PRECEDING BYTE =0, FIRST BYTE NOT=0
	HRRZ A,QSNLCN(C)
	MOVEI B,1777(A)	;END OF NAMES
	ADD A,1(A)	;BEGIN NAMES
QDIRC1:	CAMG B,A
	 JRST POPTTJ	;DONE
	SKIPN (A)
	 JRST QDIRC2	;0 NAME=NO FILE
	LDB TT,[1500,,UNRNDM(A)]	;DESC PTR
	SOS TT		;TRICK - IF DESC PTR = 0 LDB BELOW GETS 0 BECAUSE P FIELD = 44
	IDIVI TT,6
	HLL TT,SBTBL(I)	;BYTE POINTER
	ADDI TT,UDDESC-1777(B)
	LDB I,TT
	JUMPN I,QUDCLB
	ILDB I,TT
	JUMPE I,QUDCLB
QDIRC2:	ADDI A,LUNBLK
	JRST QDIRC1

IFN RP10P,[
QPOSR:	CAIL E,MBLKS
	 BUG			;TOO BIG EVEN FOR RP03
IFN DMDSK,[
	IMULI E,SECBLK
	IDIVI E,NSECS
	SETZM D
	DPB TT,[DSECT+D]
	IDIVI E,NHEDS
	DPB TT,[DSURF+D]
	DPB E,[DCYL+D]
	MOVEM E,QSPPS(Q)
	LSH E,-8
	DPB E,[DCYLXB+D]
	DPB Q,[DUNFLD+D]
]
IFE DMDSK,[
	MOVEI D,0
	IDIVI E,NBLKSC
	DPB E,[DCYL+D]
	MOVEM E,QSPPS(Q)
	LSH E,-8
	DPB E,[DCYLXB+D]
	MOVE E,TT
	IMULI E,NSSECS
	IDIVI E,NHSECS
	DPB E,[DSURF+D]
	DPB TT,[DSECT+D]
	DPB Q,[DUNFLD+D]
]
	MOVE E,D
	POPJ P,
]
IFN RH10P+RH11P,[
IFE DMDSK, .ERR CHANGE QPOSR FOR 9-SECTOR BLOCKS!
QPOSR:	CAIL E,NBLKS
	 BUG
	IDIVI E,NBLKSC
	HRLZ D,E		;CYLINDER IN LH
	MOVEM E,QSPPS(Q)
	MOVE E,TT
	IMULI E,SECBLK
	IDIVI E,NSECS
	LSH E,8
	IOR E,D			;SURFACE IN 1.9-2.4
	IOR E,TT		;SECTOR IN 1.1-1.5
	POPJ P,
] ;RH10P+RH11P

IFN DC10P,[
QPOSR:	CAIL E,NBLKS
	 BUG
	IDIVI E,NSECS
	MOVSI D,(DUNENB)
	DPB TT,[DSECT+D]
	IDIVI E,NHEDS
	DPB TT,[DSURF+D]
	SKIPGE TT,QTRAN(Q)
	 ADDI E,NCYLS+XCYLS
	DPB E,[DCYL+D]
	MOVEM E,QSPPS(TT)
	MOVE E,D
	DPB TT,[DUNFLD+E]
	DPB TT,[DUNFLD+QCHPRG]
	SKIPGE TT,QPKID(Q)
	 JRST QPOSRI
	DPB TT,[DPKID+E]
	POPJ P,

QPOSRI:	MOVE TT,QTRAN(Q)	;NEED PACK ID BEFORE PROCEEDING
	DPB TT,[DUNFLD+GPKID]
	SETOM QSKT1(TT)
	MOVEI A,TUTCYL
	SKIPGE QTRAN(Q)
	 ADDI A,NCYLS+XCYLS
	DPB A,[DCYL+GPKID]
	MOVEM A,QPOS(TT)	;WILL SEEK TO HERE AUTOMATICALLY
	MOVEM A,QPOSGL(TT)
	DATAO DC0,[DJMP GPKID]
	MOVEM Q,QSDU
	SETOM PKIDM
	JRST QINTX

QSPKID:	CONSZ DC1,1777
	 BUG		;ERRORS
	LDB TT,[DPKID+RPKID]
	MOVEM TT,QPKID(Q)
	SETOM QSDU
	JRST QINT1
]

SUBTTL DISK IOT ROUTINES

OVHMTR UUO	;MORE RANDOM UUOS

;BECAUSE .ACCESS MERELY DROPS ITS ARG IN A VARIABLE AND SETS %QAACC,
;ALL IOT ROUTINES MUST TEST %QAACC AND DO THE REAL WORK OF CHANGING
;THE ACCESS POINTER IF NECESSARY.

QBO:	MOVEI T,BLKT
	JRST QUO1

	SKIPA T,[SIOKT]
QUIO:
QUAO:	 MOVEI T,CHRKT
QUO1:	PUSH P,T
	PUSH P,TT
	PUSH P,D
	MOVE T,QSRAC(A)
	TLNE T,%QAACC+%QALNK
	 PUSHJ P,QBWRA1	;RANDOM ACCESS MODE HACK
	POP P,D
	POP P,TT
	MOVE E,QSBYTE(A)
	JSP B,CPOPJ	;TRANSFER TO CHRKT, SIOKT, OR BLKT
QBOV:	SETZ QSMPRP(A)
	QSMPRC(A)
	QSBWG
	SETZ QSBWW
	JRST QOCL
	TRNA

QBI:	MOVEI T,BLKT
	JRST QUI1

	SKIPA T,[SIOKT]
QUII:
QUAI:	 MOVEI T,CHRKTI
QUI1:	PUSH P,T
	PUSH P,TT
	MOVE T,QSRAC(A)
	TLNE T,%QAACC+%QALNK
	 PUSHJ P,QBRRA1
	POP P,TT
	MOVE E,QSBYTE(A)
	JSP B,CPOPJ		;TRANSFER TO CHRKTI, SIOKT, OR BLKT
QBIV:	QSMPRP(A)
	QSMPRC(A)
	QSBGB
	QSBRB
	JRST QICL
	SKIPG QSBFS(A)

QBWRA1:	TLNE T,%QALNK
	 JRST IOCR10
	LDB T,[$QAMOD,,QSRAC(A)]	;SET RANDOM ACCESS PNTRS ON WRITE
	SOJN T,QBWRA2		;IN ORDINARY WRITE MODE
	SKIPGE QSCRW(A)		;WAIT FOR CHANNEL TO BECOME IDLE OR HANG UP IN READ
	 PUSHJ P,UFLS		; WAITING FOR %QAACC TO TURN OFF
	SKIPL QSGL(A)
	 PUSHJ P,UFLS
	SKIPGE QSMDN(A)
	 JRST QBWRA2
	MOVE T,QRADAD(A)	;DESIRED ADDRESS
	SUB T,QFBLNO(A)		;ACTUAL ADDRESS OF BEG OF CURRENT BUFFER
	JUMPL T,QBWRA2		;XFER ON BEFORE CURRENT BLOCK
	MOVE Q,T
	SUB T,QMPBSZ(A)		;SIZE OF CURRENT BUFFER IN BYTES
	JUMPL T,QBWRA3		;SAME BLOCK AS NOW
QBWRA2:	PUSH P,R
	PUSH P,I
	PUSH P,C
	PUSHJ P,QSOCL5		;CLEAR ANY CURRENT BUFFERS ETC
	PUSHJ P,QUDULK
	MOVE Q,QRADAD(A)
	PUSHJ P,QFNTR		;LOCKS DIR IF SKIPS
	 JRST QBWRA4		;OFF END OF FILE
QBWRA5:	PUSHJ P,QUDULK
QBWRA9:	POP P,C
	POP P,I
	POP P,R
	CLEARM QSCRW(A)		;SWITCH TO WRITE OVER MODE
	MOVSI Q,%QAMPU+%QAMWO	;SET UPDATE ADR AND WRITE OVER
	IORM Q,QSRAC(A)
	CLEARM QSMPRC(A)
	MOVSI Q,%QAEFR+%QAEFW+%QAACC	;CLEAR EOF, WRITE EOF, AND ACCESS FLAGS
	ANDCAM Q,QSRAC(A)
	MOVEI Q,%QMRD1		;AND READ IN THE CURRENT BLOCK
	HRRM Q,QSRAC(A)
	POPJ P,

QBWRA4:	JUMPE Q,QBWRA0
	CAME J,Q		;MIGHT BE ADDING TO END OF FULL BLOCK
	 JRST IOCER2
	JRST QBWRA9

QBWRA0:	PUSHJ P,QLWO		;OFF END OF FILE AND WAS ACCESSING WD 0
	MOVSI C,%QAACC		;SO SWITCH TO NORMAL WRITE MODE AND
	ANDCAM C,QSRAC(A)	;TURN OFF RANDOM ACCESS BIT
	POP P,C
	POP P,I
	POP P,R
	POPJ P,

QBWRA3:	SKIPGE TT,QSMDN(A)
	 BUG			;NO BUFFER REALLY ACTIVE AT M.P.
	MOVSI J,%QAWOV
	TDNN J,QSRAC(A)
	 JRST QBWRA7
	ANDCAM J,QSRAC(A) 	;WAS WRITING IN LAST BLK PAST EOF,
	MOVN D,QSMPRC(A)	; UPDATE ACTIVE BYTE COUNT
	ADDM D,QMPBSZ(A)	;DECREASE SIZE OF BUFFER TO AMT ACTUALLY WRITTEN
QBWRA7:	LSH TT,10.		;ADDRESS OF BUFFER
	MOVE T,Q		;SAVE RELATIVE BYTE ADDR WITHIN BUFFER
	IDIVI Q,@QSBYTE(A)	;Q = WDS, J = BYTES
	ADD TT,Q		;ADDRESS OF DESIRED WORD
	HLL TT,QSBYTE(A)	;BYTE POINTER TO FIRST BYTE IN THAT WORD
	JUMPE J,.+3		;ADVANCE TO APPROPRIATE BYTE
	 IBP TT
	 SOJG J,.-1
	MOVEM TT,QSMPRP(A)
	SUB T,QMPBSZ(A)		;MINUS # BYTES LEFT IN BLOCK
	MOVNM T,QSMPRC(A)
QBRRA4:	MOVSI Q,%QAACC
	ANDCAM Q,QSRAC(A)
	POPJ P,

QSKFRC:	MOVN B,QSMPRC(A)
	ADD B,QMPBSZ(A)		;NUMBER OF BYTES WRITTEN IN BLOCK
	MOVE C,QSRAC(A)
	TLNE C,%QAMWO
	 TLNE C,%QAWOV
	  JRST [MOVEM B,QMPBSZ(A)	;IS LAST BLOCK, CHANGE LENGTH
		PUSHJ P,QOCLPD	;AND PAD IT (GUARANTEED NO-OP IF WORD MODE CHNL)
		JRST .+1 ]
	SETZM QSMPRC(A)		;BUFFER WILL BE DISPOSED OF
	MOVSI E,%QUDWM
	SKIPGE QSMDN(A)
	 SETZB B,E		;NO BUFFER AFTER ALL
	ADD B,QFBLNO(A)		;CURRENT POSITION IN FILE
	MOVSI C,%QAACC
	TDNN C,QSRAC(A)
	 MOVEM B,QRADAD(A)	;ACCESS BACK ON NEXT IOT
	IORB C,QSRAC(A)
	PUSHJ P,[TLNE C,%QAWOV	;WRITE OUT THE BUFFER
		  JUMPN E,QSBWO2
		 JRST QSBWW ]
	SKIPE QMPTC(A)
	 MOVSI E,%QUDWM
	PUSHJ P,QOCL2		;STORE QMPTC IF NECESSARY
	IORM E,QSNLCN(H)	;WRITE OUT DIR FAST IF CHANGED
	PUSHJ P,QUDULK
	JRST POPJ1

;.CALL FINISH ON DISK OUTPUT CHANNEL
QSKFIN:	HRRZ T,QSRAC(A)
	CAIN T,%QMWOV		;IF WRITE-OVER MODE
	 JRST [	SKIPE QSCRW(A)	;THEN WAIT FOR IT TO SWITCH TO READ MODE
		 PUSHJ P,UFLS
		JRST QSKFIN ]
	CAIN T,%QMWRT		;IF WRITE MODE
	 JRST [	SKIPE QSBFS(A)	;THEN WAIT FOR ALL BUFFERS TO GET WRITTEN
		 PUSHJ P,UFLS
		JRST .+1 ]
	MOVE H,QUDPR(A)
	MOVE T,MDSK		;HAS THE DIR BEEN CHANGED AND NOT WRITTEN
	MOVE T,DCHBT(T)		;YET TO THE MASTER DISK?
	TDNN T,QSNLCN(H)
	 JRST POPJ1
	MOVSI TT,%QUDWM		;YES, WRITE IT OUT IMMEDIATELY
	IORM TT,QSNLCN(H)	;AND DON'T RETURN UNTIL IT IS WRITTEN
	TDNE T,QSNLCN(H)
	 PUSHJ P,UFLS
	JRST POPJ1

QBRRA1:	TLNE T,%QALNK
	 JRST IOCR10
	SKIPGE QSMDN(A)
	 JRST QBRRA2		;NO MAIN PRGM BUFFER
	MOVE T,QRADAD(A)
	SUB T,QFBLNO(A)
	JUMPL T,QBRRA2
	MOVE Q,T
	SUB T,QMPBSZ(A)		;SIZE OF CURRENT BUFFER
	JUMPL T,QBRRA3		;SAME BLOCK AS NOW
QBRRA2:	PUSH P,R
	PUSH P,I
	PUSH P,C
	PUSHJ P,QICLW1		;STOP THE CHANNEL AND FLUSH CURRENT BUFFERS
	MOVE A,D
	CLEARM QSBFS(A)		;FLUSH POSSIBLE EXTRA AOSES WHEN PI HIT EOF
	MOVE Q,QRADAD(A)
	PUSHJ P,QFNTR
	 JRST QBRRA5		;OFF END OF FILE (DIR ALREADY UNLOCKED)
	POP P,C
	POP P,I
	POP P,R
	CLEARM QSMPRC(A)
	MOVSI Q,%QAMPU
	IORM Q,QSRAC(A)		;SET FLAG TO SET QSMPRP AND QSMPRC ON NEXT BUFFER LOAD
	MOVSI Q,%QAEFR+%QAACC	;CLEAR EOF AND .ACCESS FLAGS
	ANDCAM Q,QSRAC(A)
	MOVEI Q,%QMRD		;START READING AGAIN
	HRRM Q,QSRAC(A)
	JRST QUDULK

QBRRA5:	POP P,C
	POP P,I
	POP P,R
	CAME J,Q
	 JRST IOCER2		;ACCESS OFF END OF FILE IS ERROR
	MOVSI TT,%QAEFR		;BUT ACCESS TO EXACTLY EOF IS OK
	IORM TT,QSRAC(A)	;IMITATES WHAT QEOF DOES
	AOS QSBFS(A)
	SETZM QSMPRC(A)		;AND READS ZERO WORDS
	JRST QBRRA4

QBRRA3:	SKIPGE TT,QSMDN(A)
	 JRST QBRRA2
	JRST QBWRA7

;BLKT-SIOKT-CHRKT GET BUFFER ROUTINE FOR WRITE-OVER MODE.
QWOG1:	SKIPGE QSCRW(A)	;FETCH BLOCK WRITEOVER MODE
	 JRST QWOG2	;STILL WRITING PREVIOUS ONE, WAIT
	MOVEI Q,%QMRD1	;SWITCH INTO READ MODE
	SKIPG QSBFS(A)	;IF A BUFFER HAS TO BE READ
	 HRRM Q,QSRAC(A)
	SKIPG QSBFS(A)
	 PUSHJ P,UFLS
	POP P,A
;BLKT-SIOKT-CHRKT GET-BUFFER ROUTINE FOR DISK INPUT.
QSBGB:	MOVSI Q,%QAPAR
	TDNE Q,QSRAC(A)
	 JRST QSBGB2
	CONO PI,UTCOFF
	HRRZ Q,QBFP(A)
	JUMPE Q,[MOVSI J,%QAEFR	;SAID TO BE BUFFERS, BUT NONE THERE
		 TDNN J,QSRAC(A)
		  BUG		;SHOULDN'T HAPPEN EXCEPT AT EOF
		 JRST QSBGB5 ]
	LDB J,[MLO,,MEMBLT(Q)]
	HRRM J,QBFP(A)
	SKIPN J
	 SETZM QBFP(A)		;LAST BLOCK, LIST NOW EMPTY
	HRRZ J,MEMPNT(Q)	;GET SIZE OF BUFFER IN BYTES
	MOVEM J,QMPBSZ(A)
	LDB TT,[$QAMOD,,QSRAC(A)]
	SKIPN TT		;DONT SOS IF IN WRITE OVER MODE
	 SOS QSBFS(A)
QSBWG4:	MOVE TT,Q
	LSH TT,10.		;RETURN ADDR OF BUFFER
	MOVEM Q,QSMDN(A)
	CONO PI,UTCON
	HLL TT,QSBYTE(A)
	MOVSI Q,%QAMPU
	TDNN Q,QSRAC(A)
	 JRST SIOBG2
	MOVE Q,QRADAD(A)	;DIDDLE PNTRS TO TAKE CARE OF RANDOM ACCESS WITHIN BLOCK
	SUB Q,QFBLNO(A)		;DESIRED OFFSET WITHIN BLOCK
	SUB J,Q
	JUMPL J,IOCER2		;OFF END OF FILE
	JUMPL Q,IOCER2		;OFF FRONT OF FILE (NEGATIVE .ACCESS PNTR)
	PUSH P,J
	IDIVI Q,@QSBYTE(A)
	ADD TT,Q		;ADJUST BYTE POINTER
	JUMPE J,.+3
	 IBP TT
	 SOJG J,.-1
	POP P,J
	MOVSI Q,%QAMPU		;NOW IS SAFE TO TURN OFF FLAG
	ANDCAM Q,QSRAC(A)
	JRST SIOBG2

;GET-BUFFER DETECTED EOF ON INPUT OR WRITE-OVER.
QSBGB5:	CONO PI,UTCON
	MOVE Q,QSRAC(A)
	TLNE Q,%QALNK
	 JRST IOCR10
	LDB Q,[$QAMOD,,QSRAC(A)]
	SOJN Q,POPJ2	;ON INPUT, SKIP TWICE TO SIGNAL EOF.
	PUSHJ P,QLWO	;LEAVE WRITE OVER MODE
	JRST QSBWG	;START NORMAL WRITE

QWOG2:	SKIPGE QSCRW(A)		;WAIT FOR WRITE TO FINISH
	 PUSHJ P,UFLS
	JRST POPAJ		;NOW RECYCLE AND WAIT FOR READ

;LEAVE WRITE OVER MODE
QLWO:	MOVE H,QUDPR(A)
	PUSHJ P,QUDLK
	CONO PI,UTCOFF
	SETZM QBFP(A)		;WRITEOVER MODE EXTEND FILE (SWITCH TO NORMAL MODE)
	CLEARM QSBFS(A)
	SETOM QSCRW(A)
	MOVE Q,QSLGL(A)
	HRRZM Q,QMPTN(A)	;STORE BASE TRACK
	MOVSI Q,%QAEFR+%QAMWO	;CLEAR EOF AND WRITEOVER
	ANDCAM Q,QSRAC(A)
	MOVEI Q,%QMWRT		;NORMAL WRITE MODE
	HRRM Q,QSRAC(A)
	CLEARM QMPTC(A)		;TRACK COUNT FOR TAKE N
	SETOM QMTTR(A)		;NO TRACK RESERVED
	SETOM QMFTP(A)		;GET NEW DECADE RESERVATION
	CONO PI,UTCON
	MOVE TT,QDIRP(A)	;SEE IF GARBAGE COLLECTION NEEDED
	PUSH P,R
	PUSH P,I
	PUSHJ P,QMPDC1		;CONV CHAR ADR TO BYTE PNTR (AND LDB IN R)
	POP P,I
	SKIPE R
	 BUG
	MOVE R,QDIRP(A)
	ADDI R,NXLBYT+2
	CAMGE R,@QSNLCN(H)
	 JRST QLWO1		;DON'T NEED TO EXPAND F.S.
	MOVEI R,LUNBLK(TT)
	MOVE Q,QSNLCN(H)
	SUBI R,(Q)
	CAML R,UDNAMP(Q)
	 JRST QSBWG7		;DON'T HAVE ROOM.  GC
	MOVEI R,3*6		;HAVE ROOM
	ADDM R,(Q)
QLWO1:
REPEAT NXLBYT+2,[
	ILDB R,TT
	JUMPN R,QSBWG7
]
	JRST QSBWG8		;OK

QSBWG7:	MOVSI R,%QAFUL		;NO ROOM, GC BEFORE COMMITTING NEXT TRACK
	IORM R,QSRAC(A)
QSBWG8:	POP P,R
	JRST QUDULK

QSBGB2:	ANDCAM Q,QSRAC(A)	;DISK READ ERROR
	JRST IOCER3		;TELL USER

;DISK OUTPUT GET-BUFFER ROUTINE.  PRESERVES D FOR BENEFIT OF PDUMP.
QSBWG:	MOVE Q,QSRAC(A)
	TLNE Q,%QALNK+%QAACC	;DON'T WRITE TO LINK, DON'T LET PDUMP GET
	 JRST IOCR10		; FAKED OUT BY USE FORCE OR ACCESS
	PUSH P,A
	TLNE Q,%QAMWO
	 JRST QWOG1		;JUMP IF WRITE OVER MODE
	HRRZ Q,QBFP(A)		;NORMAL MODE
	JUMPE Q,QSBWG1		;CAN ALWAYS HAVE ONE BUFFER
	MOVE Q,QWBUFS
	CAML Q,QWBFMX
	 JRST POPAJ		;TOO MANY WRITE BUFFERS QUEUED
QSBWG1:	PUSHJ P,QSTWG
	PUSHJ P,TCALL
	  JRST IOMQ
	 JRST QSBWG5		;MEM FROZE OR NOT AVAILABLE
	MOVE Q,(P)		;DISK CHNL #
	DPB Q,[MNUMB,,MEMBLT(A)]
	MOVEI Q,MU23B
	DPB Q,[MUR,,MEMBLT(A)]
	MOVE Q,A
	POP P,A
	AOS QSBFS(A)
	AOS QWBUFS
	HRRZ J,QSBYTE(A)
	IMULI J,2000
	MOVEM J,QMPBSZ(A)	;FRESH BLOCK SIZE
	JRST QSBWG4

QSTWG:	MOVSI Q,%QAFUL
	TDNE Q,QSRAC(A)		;ROUTINE TO COMMIT A TRACK
	 JRST QSTWG1		;DIR FULL, NEED GC BEFORE GROWING FILE
	MOVE H,QUDPR(A)		;IF THIS DIRECTORY HAS AN ALLOCATION
	MOVE Q,QSNLCN(H)	; ENFORCE IT
IFN QRSRVP,[
	HRRZ J,UDALLO(Q)
	JUMPE J,QSTWG0		;NO ALLOCATION
	HRRZ H,UDBLKS(Q)	;GET BLOCKS USED
	CAML H,J
	 JRST IOCR13		;HAS ALLOCATION AND USER IS TRYING TO EXCEED IT
]
QSTWG0:	MOVE J,QDSKN(A)
	SOSL QSFT(J)		;RESERVE A TRACK
	 POPJ P,
	AOS QSFT(J)		;DISK FULL, GIVE BACK THE TRACK
	SKIPE QFBTS		;WAIT FOR ANY PENDING FILE DELETIONS
	 PUSHJ P,UFLS		; TO COMPLETE AND FREE THEIR BLOCKS
	SOSL QSFT(J)		;GOT SPACE NOW?
	 POPJ P,
	AOS QSFT(J)		;NO, GIVE UP AND ERR OUT
	JRST IOCER9

QSTWG1:	MOVE H,QUDPR(A)
	PUSHJ P,QUDLK
	PUSHJ P,QGC
	 JRST IOCR12		;DIR FULL
	PUSHJ P,QUDULK
	ANDCAM Q,QSRAC(A) .SEE %QAFUL
	JRST QSTWG

QSBWG5:	POP P,A			;GET BACK QSK CHANNEL #
	MOVE J,QDSKN(A)
	AOS QSFT(J)		;NO MEMORY; GIVE EVERYTHING BACK
	POPJ P,

;DISK OUTPUT RELEASE-BUFFER ROUTINE FOR BLKT, SIOKT, CHRKT.
QSBWW:	SKIPGE QSMDN(A)		;WRITE BUFFER FROM MN PROG TO CHAIN FOR PI
	 POPJ P,		;NO WRITE BUFFER ACTIVE
	LDB TT,[$QAMOD,,QSRAC(A)]
	SOJE TT,QSBWO1		;WRITE OVER MODE
	SKIPL D,QMTTR(A)
	 JRST QTG1		;TRACK ALREADY RESERVED
	PUSH P,I
	MOVE I,QDSKN(A)
	PUSHJ P,QGTRK		;GET A TRACK
	POP P,I
	MOVEM D,QMTTR(A)
QTG1:	MOVE H,QUDPR(A)		;TRACK IN D
	PUSHJ P,QUDLK
	MOVE TT,QSNLCN(H)
	AOS UDBLKS(TT)
	MOVE TT,QMPTN(A)	;GET PREVIOUS TRACK#
	CAIN D,1(TT)
	 JRST QTG2		;THIS TRACK IS CONSECUTIVE
	SKIPN QMPTC(A)		;THIS TRACK NOT CONSEC CHECK COUNT OF PREV CONSEC BLOCKS
	 JRST QTG3
	PUSH P,D		;NON-ZERO SO STORE IN USER DIRECTORY
	MOVE D,QMPTC(A)
	CLEARM QMPTC(A)
	PUSHJ P,QUDS
	POP P,D
QTG3:	MOVE J,QMTTR(A)		;DESCRIPTOR WILL BE STORED
	MOVEM J,QMPTN(A)
	SUB D,QMPTN(A)		;CAN TRACK BE SKIPPED TO?
	SOJL D,QTG4		;CAN'T BE SKIPPED TO (NOTE QMPTN HAS NOT BEEN AOS'D)
	CAILE D,UDWPH-UDTKMX-1
	 JRST QTG4		;ALSO NO
	ADDI D,UDTKMX		;NEXT BLOCK WITHIN N
	PUSHJ P,QUDS		;STORE APPROPRIATE SKIP
QTG5A:
QTG5:	MOVE Q,QSMDN(A)		;CORE BLOCK NUM BEING WRITTEN
	MOVE D,QMPTN(A)		;TRACK NUM OF BLOCK ADDING TO FILE
	HRLZM D,MEMPNT(Q)	;STORE TRACK NUM
	SETOM QMTTR(A)		;INDICATE TRACK USED
	MOVE J,QMPBSZ(A)	;LENGTH OF THIS BLOCK
	ADDM J,QFBLNO(A)	;INCR BYTE ADR OF START OF CURRENT BLOCK IN FILE
	HRRM J,MEMPNT(Q)	;SAVE BYTE COUNT
	PUSH P,R
	IDIVI J,@QSBYTE(A)	;CONVERT TO WORD COUNT
	JUMPE R,QTG7		;EXACT MULTIPLE OF WORD
	AOS J
	MOVNS R
	ADDI R,@QSBYTE(A)	;# BYTES RESIDUE IN LAST WORD
QTG7:	DPB J,[MWC,,MEMBLT(Q)]	;STORE ACTIVE WORD COUNT
	MOVE D,QSRAC(A)
	LDB TT,[$QAMOD,,QSRAC(A)]
	TLNN D,%QALBK		;LAST BLOCK OF WRITE OVER POSSIBLY
	 SOJE TT,QTG6		;WRITEOVER MODE AND NOT LAST BLOCK, DONT UPDATE LAST BLOCK WORD COUNT
	MOVE H,QUDPR(A)		;SET UP DIRECTORY CHANNEL
	MOVE D,QUDFPR(A)
	ADD D,QSNLCN(H)
	DPB J,[UNWRDC+UNRNDM(D)] ;SET WORD COUNT OF LAST BLOCK
	LDB Q,[QSBSIZ(A)]
	PUSHJ P,QBENC		;ENCODE BYTE CRUFT
	DPB Q,[UNBYTE+UNREF(D)]
	MOVE J,QACTB
	IORM J,QSNLCN(H)
QTG6:	POP P,R
	PUSHJ P,QUDULK
	MOVE Q,QSMDN(A)
	MOVEI J,.BM MLO
	ANDCAM J,MEMBLT(Q)	;SET END OF LIST INDICATOR IN MEMORY BLOCK
	CONO PI,UTCOFF
	HLRZ J,QBFP(A)		;STORE MEMORY BLOCK IN OUTPUT LIST
	JUMPE J,QSBWW1
	DPB Q,[MLO,,MEMBLT(J)]
QSBWW2:	HRLM Q,QBFP(A)
	CONO PI,UTCON
	SETOM QSMDN(A)		;MN PROG BUFFER NOW WRITTEN
	JRST QSTRTR		;START ACTION IF 2311 IDLE AND RETURN

QSBWO1:	MOVE Q,QSRAC(A)		;PUT BUFFER IN WRITE-OVER MODE
	TLNE Q,%QALBK		;IF NOT HACKING LAST BLOCK
	 TLNE Q,%QAEFW		;OR HACKING CLOSE
	  JRST QSBWO2		;THEN JUST WRITE IT
	HRRZ D,QSBYTE(A)
	IMULI D,2000
	SUB D,QMPBSZ(A)
	JUMPE D,QSBWO2		;BLOCK REALLY FULL
	TLNE Q,%QAMPU		;DID WE IOCER2 AT QSBWG4?
	 JRST IOCER2		;YES, QSMPRP NOT SET UP, DON'T WRITE ANYTHING
	MOVEM D,QSMPRC(A)	;LAST BLOCK NOT REALLY USED UP ("SPRUNG BACK TO LIFE")
	ADDM D,QMPBSZ(A)	;EXTRA ROOM IN LAST BLOCK TO FILL UP
	MOVSI D,%QAWOV
	IORM D,QSRAC(A)		;FILLING LAST BLK PAST ORIG EOF
	POPJ P,			;FINISH OUT BLOCK

QSBWO2:	MOVSI D,%QAWOV
	ANDCAM D,QSRAC(A)
	MOVE D,QSLGL(A)		;WRITE TRACK BACK WHERE IT CAME FROM
	MOVEM D,QMPTN(A)	;PUT TRACK NO WHERE QTG5 CAN FIND IT
	MOVE H,QUDPR(A)
	PUSHJ P,QUDLK
	SETOM QSCRW(A)		;SWITCH TO WRITE MODE
	MOVEI Q,%QMWOV
	HRRM Q,QSRAC(A)
	JRST QTG5


QTG4:	LDB D,[140500,,QMTTR(A)] ;WRITE LOAD ADDRESS
	IORI D,40
	PUSHJ P,QUDS
	LDB D,[060600,,QMTTR(A)]
	PUSHJ P,QUDS
	LDB D,[0600,,QMTTR(A)]
	PUSHJ P,QUDS
	JRST QTG5A
 
QGTRK:	PUSHJ P,QTLOCK		;GET TRK CHNL IN A DSK IN I RET IN D CLOBBERS E,TT,Q,J,B
QGTK4:	PUSH P,R		;ENTER HERE FROM SWAP OUT WITH UTCOFF
	PUSH P,B
	PUSH P,E
	HRRZ R,QTUTO(I)
QGTK4A:	SKIPGE Q,QMFTP(A)
	 JRST QGTK3A		;FIRST TRACK
	IDIVI Q,DECADE
	HRREI J,-DECADE(J)	;COMPUTE TRACKS REMAINING IN DECADE
	MOVE D,QMFTP(A)
	PUSHJ P,TUTPNT
	ADD D,[TUTBYT_14,,]	;MAKE INTO ILDB-TYPE POINTER
	MOVE TT,QMFTP(A)
	EXCH D,TT
QGTK3:	CAML D,QLASTB(R)
	 JRST QGTK3A		;REACHED END OF DISK
	ILDB Q,TT
	JUMPE Q,QGTK2
	AOJGE J,QGTK3A		;DECADE OUT
	AOJA D,QGTK3

QGTK3A:	CAILE A,NQCHN
	 JRST QGTK3C		;SWAP CHANNEL
QGTK3D:	MOVEI Q,DECADE
	ADDB Q,QTUTP(R)
QGTK3B:	MOVEM Q,QMFTP(A)
	CAMGE Q,QLASTB(R)
	 JRST QGTK4A
	MOVE Q,QSWAPA(R)	;REACHED END OF DSK, RESET TO END OF SWAPPING AREA
	ADDI Q,DECADE-1		;ROUND UP TO A MULTIPLE OF A DECADE
	IDIVI Q,DECADE
	IMULI Q,DECADE
	MOVEM Q,QTUTP(R)
	JRST QGTK3B

QGTK2:	MOVEI B,1
	DPB B,TT
	MOVEM D,QMFTP(A)
	MOVE J,DCHBT(I)
	IORM J,QTUTO(I)
	POP P,E
	POP P,B
	POP P,R
	CAILE A,NQCHN
	 POPJ P,		;CALLED FROM SWAP OUT
	JRST QTULK

QGTK3C:	CAML D,QSWAPA(R)
	 JRST QGTK3D		;IN NON-SWAP AREA
	AOS D
	CAMN D,QSWAPA(R)
	 MOVEI D,NUDSL
	MOVEM D,QMFTP(A)
	JRST QGTK4A
;STORE CHR IN D CHNL IN A USER DIR PNTR IN H
;CANNOT PCLSR NOR GC

QUDS:	PUSH P,I
	PUSH P,Q
	MOVE I,QDIRP(A)
	CAIGE I,2000*UFDBPW
	CAML I,@QSNLCN(H)	;UDESCP
	 BUG			;WRITING ABOVE F.S. PNTR?
	AOS QDIRP(A)
	IDIVI I,UFDBPW
	MOVE Q,QBTBL(Q)
	ADD Q,I
	SKIPL I,QSNLCN(H)
	 BUG			;UFD WAS NOT LOCKED
	ADDI Q,UDDESC(I)
	LDB I,Q
	CAIE I,UDWPH		;OK TO CLOBBER UDWPH (NORMALLY DOES AT START OF FILE)
	 JUMPN I,[JRST 4,.]	;ABOUT TO CLOBBER SOMETHING IN UFD
	DPB D,Q
	ILDB I,Q		;MAKE SURE FILE DESC WILL ALWAYS BE FOLLOWED BY ZERO
	SKIPE I
	 BUG
REPEAT NXLBYT+2,[		;MAKE SURE THERE'S ROOM FOR ONE MORE TRACK
	ILDB I,Q
	JUMPN I,QUDS1
]
	MOVE I,QDIRP(A)
	ADDI I,NXLBYT+2
	CAMGE I,@QSNLCN(H)	;UDESCP
	 JRST QUDSX		;DESC AREA NOT COLLIDING WITH LIM OF DESC SPACE
	MOVEI Q,LUNBLK(Q)
	MOVE I,QSNLCN(H)
	SUBI Q,(I)
	CAML Q,UDNAMP(I)
	 JRST QUDS1		;NO ROOM.
	MOVEI Q,3*UFDBPW	;EXPAND DESC AREA
	ADDM Q,UDESCP(I)
	JRST QUDSX

QUDS1:	MOVSI Q,%QAFUL		;NEED GC BEFORE STARTING ON NEXT TRACK
	IORM Q,QSRAC(A)
QUDSX:	MOVE Q,QACTB
	IORM Q,QSNLCN(H)	;SET DIRECTORY CHANGED
	POP P,Q
	POP P,I
	POPJ P,

QSBWW1:	HRRM Q,QBFP(A)
	JRST QSBWW2

QSBRB1:	MOVE A,D
QSBRB:	PUSH P,A
	SKIPGE A,QSMDN(A)
	 JRST POPAJ
	PUSHJ P,MEMR
	POP P,A
	HRRZ TT,QMPBSZ(A)
	ADDM TT,QFBLNO(A)	;INCREMENT BYTE ADR IN FILE OF BEG OF NEXT BLOCK
	SETOM QSMDN(A)
	JRST QSTRTR

QTG2:	MOVE J,QMTTR(A)		;INDICATE WE WILL STORE DESCRIPTOR FOR NEXT TRACK.
	MOVEM J,QMPTN(A)
	AOS D,QMPTC(A)		;NEXT BLOCK IS CONSECUTIVE
	CAIGE D,UDTKMX
	 JRST QTG5A
	SETZM QMPTC(A)		;MAX # CONSECUTIVE BLOCKS, STORE DESC NOW
	PUSHJ P,QUDS
	JRST QTG5A

SUBTTL	DIRHNG DEVICE

;WHENEVER THE DIRECTORY SPECIFIED AS THE SNAME WHEN DIRHNG IS OPENED
;IS MODIFIED, YOU GET AN INTERRUPT ON THE DIRHNG DEVICE CHANNEL.

;HERE TO OPEN THE DIRHNG DEVICE.  IOCHNM ADDR IN R, SNAME IN USYSN1(U).
DIRHO:	MOVE C,USYSN1(U)
	PUSHJ P,QFL	;LOOK UP THIS DIRECTORY
	 JRST OPNL20	;CALL FAILS IF DIRECTORY DOES NOT EXIST.
	HRRZM J,IOCHST-IOCHNM(R)	;DIR EXISTS;  STORE TRACK NUMBER IN IOCHST.
DIRHP1:	CONO PI,CLKOFF
	HLLZ A,DIRHNG	;ADD THIS CHANNEL'S IOCHNM TO THE LIST OF DIRHNG CHANNELS.
	HRRI A,DNDIRH
	MOVEM A,(R)	;SET UP THE IOCHNM WITH IOTTB INDEX AND LIST CHAIN.
	HRLZM R,DIRHNG
	JRST CLKOJ1

;CLOSE A DIRHNG DEVICE CHANNEL.
DIRHCL:	PUSHJ P,DIRHPS	;REMOVE IT FROM THE LIST, AND
	SETZM (R)	;MARK IT CLOSED.
	POPJ P,

;IOPUSH A DIRHNG DEVICE CHANNEL.  REMOVE IT FROM THE LIST OF ALL SUCH.
DIRHPS:	MOVEI A,DIRHNG
	CONO PI,CLKOFF
;FIND THE PLACE THAT POINTS AT THIS IOCHNM WORD.
DIRHC1:	HLRZ B,(A)
	CAIN B,(R)
	 JRST DIRHC2
	SKIPN A,B	;END OF LIST REACHED =>
	 BUG		;THIS IOCHNM ISN'T IN THE LIST.  BUT IT IS SUPPOSED TO BE!
	JRST DIRHC1

;A POINTS AT IOCHNM THAT POINTS AT THIS ONE.  PATCH THIS ONE OUT OF LIST.
DIRHC2:	HLRZ B,(B)	;THE ONE AFTER THIS ONE
	HRLM B,(A)
	JRST CLKONJ

;IOPUSH OR IOPOP THE DIRHNG CHANNEL WITH R -> IOCHNM WORD.  I SAYS WHICH OPERATION.
DIRHIP:	JUMPE I,DIRHPS	;IOPUSH REMOVES CHANNEL FROM LIST OF ALL DIRHNG CHANNELS.

;IOPOP A DIRHNG DEVICE CHANNEL.  JUST LIKE OPENING IT EXCEPT THAT
;THE DIRECTORY TRACK NUMBER IS ALREADY IN THE IOCHST.
;ALSO, WE SHOULD GIVE AN INTERRUPT NOW,
;IN CASE THE DIRECTORY WAS WRITTEN IN WHILE THE CHANNEL WAS PUSHED.
DIRHPP:	PUSHJ P,DIRHP1		;FIRST, REOPEN THE CHANNEL.  LINK IT INTO THE DIRHNG LIST.
	 BUG
	MOVE A,R
	SUBI A,IOCHNM(U)	;COMPUTE CHANNEL NUMBER FROM IOCHNM WORD ADDRESS.
	MOVE A,CHNBIT(A)	;GET BIT CORRESPONDING TO CHANNEL.
	AND A,MSKST2(U)		;GIVE THE INT IF THE INT IS ENABLED.
	IORM A,IFPIR(U)
	POPJ P,

;HERE WITH A/ DIRECTORY'S TRACK NUMBER (AS RETURNED BY QFL)
;TO SIGNAL A WRITE IN THAT DIRECTORY TO ALL DIRHNG DEVICES THAT ARE LOOKING.
;CLOBBERS B, C, D.
DIRSIG:	CONO PI,CLKOFF
	MOVEI B,DIRHNG
DIRSI0:	HLRZ B,(B)	;GET IOCHNM ADDR OF NEXT DIRHNG DEVICE.
	JUMPE B,CLKONJ
	HRRZ C,IOCHST-IOCHNM(B)
	CAIE C,(A)	;DOES IT LOOK AT THIS DIRECTORY?
	 JRST DIRSI0
	MOVEI C,-IOCHNM(B)	;YES => FIGURE OUT USER INDEX OF JOB IT BELONGS TO.
	IDIVI C,LUBLK
	IMULI C,LUBLK	;C NOW HAS USER INDEX.
	MOVNI D,IOCHNM(C)
	ADD D,B		;D GETS CHANNEL NUMBER
	MOVE D,CHNBIT(D)
	AND D,MSKST2(C)	;INTERRUPT THE USER ON THAT CHANNEL, IF IT'S ENABLED.
	IORM D,IFPIR(C)
	JRST DIRSI0
